diff --git a/CMakeLists.txt b/CMakeLists.txt index d7efa3a6b4..9921f7983c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,6 +448,16 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) # Get available platfroms include(platforms.cmake) +if(UNIX) + # prelim headers detection + if(NOT ("${BRANCH_TYPE}" STREQUAL "")) + set(NEO_ENABLE_i915_PRELIM_DETECTION TRUE) + elseif(NOT DEFINED NEO_ENABLE_i915_PRELIM_DETECTION) + set(NEO_ENABLE_i915_PRELIM_DETECTION FALSE) + endif() + message(STATUS "i915 prelim headers detection: ${NEO_ENABLE_i915_PRELIM_DETECTION}") +endif() + get_filename_component(I915_INCLUDES_DIR "${NEO_SOURCE_DIR}/third_party${BRANCH_DIR_SUFFIX}uapi" ABSOLUTE) message(STATUS "i915 includes dir: ${I915_INCLUDES_DIR}") @@ -793,15 +803,6 @@ else() set(NEO_EXTRA_LIBS dl pthread rt) endif() -if(UNIX) - # prelim headers detection - if(NOT ("${BRANCH_TYPE}" STREQUAL "")) - set(NEO_ENABLE_i915_PRELIM_DETECTION TRUE) - elseif(NOT DEFINED NEO_ENABLE_i915_PRELIM_DETECTION) - set(NEO_ENABLE_i915_PRELIM_DETECTION FALSE) - endif() - message(STATUS "i915 prelim headers detection: ${NEO_ENABLE_i915_PRELIM_DETECTION}") -endif() add_subdirectory_unique(shared) if(NEO_BUILD_WITH_OCL) diff --git a/level_zero/core/source/debugger/linux/debugger_l0_linux.cpp b/level_zero/core/source/debugger/linux/debugger_l0_linux.cpp index 8fa8d0752a..0b89a1f665 100644 --- a/level_zero/core/source/debugger/linux/debugger_l0_linux.cpp +++ b/level_zero/core/source/debugger/linux/debugger_l0_linux.cpp @@ -28,7 +28,7 @@ bool DebuggerL0::initDebuggingInOs(NEO::OSInterface *osInterface) { void DebuggerL0::registerElf(NEO::DebugData *debugData, NEO::GraphicsAllocation *isaAllocation) { if (device->getRootDeviceEnvironment().osInterface.get() != nullptr) { auto drm = device->getRootDeviceEnvironment().osInterface->getDriverModel()->as(); - auto handle = drm->registerResource(NEO::Drm::ResourceClass::Elf, debugData->vIsa, debugData->vIsaSize); + auto handle = drm->registerResource(NEO::DrmResourceClass::Elf, debugData->vIsa, debugData->vIsaSize); static_cast(isaAllocation)->linkWithRegisteredHandle(handle); } } @@ -39,7 +39,7 @@ bool DebuggerL0::attachZebinModuleToSegmentAllocations(const StackVecgetRootDeviceEnvironment().osInterface->getDriverModel()->as(); uint32_t segmentCount = static_cast(allocs.size()); - moduleHandle = drm->registerResource(NEO::Drm::ResourceClass::L0ZebinModule, &segmentCount, sizeof(uint32_t)); + moduleHandle = drm->registerResource(NEO::DrmResourceClass::L0ZebinModule, &segmentCount, sizeof(uint32_t)); for (auto &allocation : allocs) { auto drmAllocation = static_cast(allocation); diff --git a/level_zero/core/source/dll/linux/CMakeLists.txt b/level_zero/core/source/dll/linux/CMakeLists.txt index 527de7e5d2..6d5d5f6176 100644 --- a/level_zero/core/source/dll/linux/CMakeLists.txt +++ b/level_zero/core/source/dll/linux/CMakeLists.txt @@ -7,7 +7,16 @@ set(L0_SRCS_DLL_LINUX ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/debugger_l0_linux.cpp - ${NEO_SOURCE_DIR}/level_zero/tools/source/debug/linux/${BRANCH_DIR_SUFFIX}debug_session_linux_helper.cpp ) +if(NEO_ENABLE_i915_PRELIM_DETECTION) + list(APPEND L0_SRCS_DLL_LINUX + ${NEO_SOURCE_DIR}/level_zero/tools/source/debug/linux/prelim/debug_session_linux_helper.cpp + ) +else() + list(APPEND L0_SRCS_DLL_LINUX + ${NEO_SOURCE_DIR}/level_zero/tools/source/debug/linux/debug_session_linux_helper.cpp + ) +endif() + set_property(GLOBAL PROPERTY L0_SRCS_DLL_LINUX ${L0_SRCS_DLL_LINUX}) diff --git a/level_zero/core/test/unit_tests/sources/debugger/linux/test_l0_debugger_linux.cpp b/level_zero/core/test/unit_tests/sources/debugger/linux/test_l0_debugger_linux.cpp index 6a433973f9..228a016ac4 100644 --- a/level_zero/core/test/unit_tests/sources/debugger/linux/test_l0_debugger_linux.cpp +++ b/level_zero/core/test/unit_tests/sources/debugger/linux/test_l0_debugger_linux.cpp @@ -198,12 +198,12 @@ TEST_F(L0DebuggerLinuxTest, givenAllocationsWhenAttachingZebinModuleThenAllAlloc kernelAllocs.push_back(&isaAllocation2); drmMock->registeredDataSize = 0; - drmMock->registeredClass = NEO::Drm::ResourceClass::MaxSize; + drmMock->registeredClass = NEO::DrmResourceClass::MaxSize; EXPECT_TRUE(device->getL0Debugger()->attachZebinModuleToSegmentAllocations(kernelAllocs, handle)); EXPECT_EQ(sizeof(uint32_t), drmMock->registeredDataSize); - EXPECT_EQ(NEO::Drm::ResourceClass::L0ZebinModule, drmMock->registeredClass); + EXPECT_EQ(NEO::DrmResourceClass::L0ZebinModule, drmMock->registeredClass); const auto containsModuleHandle = [handle](const auto &bufferObject) { const auto &bindExtHandles = bufferObject.getBindExtHandles(); diff --git a/level_zero/tools/source/debug/linux/CMakeLists.txt b/level_zero/tools/source/debug/linux/CMakeLists.txt index 8015df8715..444814b5fc 100644 --- a/level_zero/tools/source/debug/linux/CMakeLists.txt +++ b/level_zero/tools/source/debug/linux/CMakeLists.txt @@ -8,8 +8,20 @@ if(UNIX) target_sources(${L0_STATIC_LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt - ${CMAKE_CURRENT_SOURCE_DIR}/${BRANCH_DIR_SUFFIX}/debug_session.cpp ) - add_subdirectories() + if(NEO_ENABLE_i915_PRELIM_DETECTION) + target_sources(${L0_STATIC_LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/prelim/debug_session.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/prelim/debug_session.h + ${CMAKE_CURRENT_SOURCE_DIR}/prelim/drm_helper.cpp + ) + else() + target_sources(${L0_STATIC_LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/debug_session.cpp + ) + endif() + endif() diff --git a/level_zero/tools/source/debug/linux/prelim/debug_session.cpp b/level_zero/tools/source/debug/linux/prelim/debug_session.cpp new file mode 100644 index 0000000000..4de38d6eca --- /dev/null +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.cpp @@ -0,0 +1,1485 @@ +/* + * Copyright (C) 2022 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/tools/source/debug/linux/prelim/debug_session.h" + +#include "shared/source/debug_settings/debug_settings_manager.h" +#include "shared/source/helpers/array_count.h" +#include "shared/source/helpers/string.h" +#include "shared/source/memory_manager/memory_manager.h" +#include "shared/source/os_interface/linux/drm_debug.h" +#include "shared/source/os_interface/linux/ioctl_helper.h" +#include "shared/source/os_interface/os_interface.h" +#include "shared/source/os_interface/os_thread.h" + +#include "level_zero/core/source/device/device.h" +#include "level_zero/core/source/device/device_imp.h" +#include "level_zero/core/source/hw_helpers/l0_hw_helper.h" +#include "level_zero/include/zet_intel_gpu_debug.h" +#include "level_zero/tools/source/debug/linux/prelim/drm_helper.h" +#include + +#include "common/StateSaveAreaHeader.h" + +#include +#include + +namespace L0 { + +DebugSession *createDebugSessionHelper(const zet_debug_config_t &config, Device *device, int debugFd); + +DebugSessionLinux::DebugSessionLinux(const zet_debug_config_t &config, Device *device, int debugFd) : DebugSessionImp(config, device), fd(debugFd) { + ioctlHandler.reset(new IoctlHandler); + + for (size_t i = 0; i < arrayCount(euControlInterruptSeqno); i++) { + euControlInterruptSeqno[i] = invalidHandle; + } +}; +DebugSessionLinux::~DebugSessionLinux() { + closeAsyncThread(); +} + +DebugSession *DebugSession::create(const zet_debug_config_t &config, Device *device, ze_result_t &result) { + if (device->getOsInterface().isDebugAttachAvailable()) { + struct prelim_drm_i915_debugger_open_param open = {}; + open.pid = config.pid; + open.events = 0; + + auto debugFd = DrmHelper::ioctl(device, PRELIM_DRM_IOCTL_I915_DEBUGGER_OPEN, &open); + if (debugFd >= 0) { + PRINT_DEBUGGER_INFO_LOG("PRELIM_DRM_IOCTL_I915_DEBUGGER_OPEN: open.pid: %d, open.events: %d, debugFd: %d\n", + open.pid, open.events, debugFd); + + auto debugSession = createDebugSessionHelper(config, device, debugFd); + result = debugSession->initialize(); + + if (result != ZE_RESULT_SUCCESS) { + debugSession->closeConnection(); + delete debugSession; + debugSession = nullptr; + } else { + debugSession->startAsyncThread(); + } + return debugSession; + } else { + auto reason = DrmHelper::getErrno(device); + PRINT_DEBUGGER_ERROR_LOG("PRELIM_DRM_IOCTL_I915_DEBUGGER_OPEN failed: open.pid: %d, open.events: %d, retCode: %d, errno: %d\n", + open.pid, open.events, debugFd, reason); + result = DebugSessionLinux::translateDebuggerOpenErrno(reason); + } + } else { + result = ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; + } + return nullptr; +} + +ze_result_t DebugSessionLinux::translateDebuggerOpenErrno(int error) { + + ze_result_t result = ZE_RESULT_ERROR_UNKNOWN; + switch (error) { + case ENODEV: + result = ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; + break; + case EBUSY: + result = ZE_RESULT_ERROR_NOT_AVAILABLE; + break; + case EACCES: + result = ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS; + break; + } + return result; +} + +int DebugSessionLinux::ioctl(unsigned long request, void *arg) { + return ioctlHandler->ioctl(fd, request, arg); +} + +ze_result_t DebugSessionLinux::readGpuMemory(uint64_t vmHandle, char *output, size_t size, uint64_t gpuVa) { + prelim_drm_i915_debug_vm_open vmOpen = { + .client_handle = static_cast(clientHandle), + .handle = static_cast(vmHandle), + .flags = PRELIM_I915_DEBUG_VM_OPEN_READ_ONLY}; + + int vmDebugFd = ioctl(PRELIM_I915_DEBUG_IOCTL_VM_OPEN, &vmOpen); + if (vmDebugFd < 0) { + PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_VM_OPEN failed = %d\n", vmDebugFd); + return ZE_RESULT_ERROR_UNKNOWN; + } + + int64_t retVal = 0; + auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); + gpuVa = gmmHelper->decanonize(gpuVa); + + if (NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.get()) { + uint64_t alignedMem = alignDown(gpuVa, MemoryConstants::pageSize); + uint64_t alignedDiff = gpuVa - alignedMem; + uint64_t alignedSize = size + alignedDiff; + + void *mappedPtr = ioctlHandler->mmap(NULL, alignedSize, PROT_READ, MAP_SHARED, vmDebugFd, alignedMem); + + if (mappedPtr == MAP_FAILED) { + PRINT_DEBUGGER_ERROR_LOG("Reading memory failed, errno = %d\n", errno); + retVal = -1; + } else { + char *realSourceVA = static_cast(mappedPtr) + alignedDiff; + retVal = memcpy_s(output, size, static_cast(realSourceVA), size); + ioctlHandler->munmap(mappedPtr, alignedSize); + } + } else { + size_t pendingSize = size; + uint8_t retry = 0; + const uint8_t maxRetries = 3; + size_t retrySize = size; + do { + PRINT_DEBUGGER_MEM_ACCESS_LOG("Reading (pread) memory from gpu va = %#" PRIx64 ", size = %zu\n", gpuVa, pendingSize); + retVal = ioctlHandler->pread(vmDebugFd, output, pendingSize, gpuVa); + output += retVal; + gpuVa += retVal; + pendingSize -= retVal; + if (retVal == 0) { + if (pendingSize < retrySize) { + retry = 0; + } + retry++; + retrySize = pendingSize; + } + } while (((retVal == 0) && (retry < maxRetries)) || ((retVal > 0) && (pendingSize > 0))); + + if (retVal < 0) { + PRINT_DEBUGGER_ERROR_LOG("Reading memory failed, errno = %d\n", errno); + } + + retVal = pendingSize; + } + + NEO::SysCalls::close(vmDebugFd); + + return (retVal == 0) ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_UNKNOWN; +} + +ze_result_t DebugSessionLinux::writeGpuMemory(uint64_t vmHandle, const char *input, size_t size, uint64_t gpuVa) { + prelim_drm_i915_debug_vm_open vmOpen = { + .client_handle = static_cast(clientHandle), + .handle = static_cast(vmHandle), + .flags = PRELIM_I915_DEBUG_VM_OPEN_READ_WRITE}; + + int vmDebugFd = ioctl(PRELIM_I915_DEBUG_IOCTL_VM_OPEN, &vmOpen); + if (vmDebugFd < 0) { + PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_VM_OPEN failed = %d\n", vmDebugFd); + return ZE_RESULT_ERROR_UNKNOWN; + } + + int64_t retVal = 0; + auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); + gpuVa = gmmHelper->decanonize(gpuVa); + + if (NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.get()) { + uint64_t alignedMem = alignDown(gpuVa, MemoryConstants::pageSize); + uint64_t alignedDiff = gpuVa - alignedMem; + uint64_t alignedSize = size + alignedDiff; + + void *mappedPtr = ioctlHandler->mmap(NULL, alignedSize, PROT_WRITE, MAP_SHARED, vmDebugFd, alignedMem); + + if (mappedPtr == MAP_FAILED) { + PRINT_DEBUGGER_ERROR_LOG("Writing memory failed, errno = %d\n", errno); + retVal = -1; + } else { + char *realDestVA = static_cast(mappedPtr) + alignedDiff; + retVal = memcpy_s(static_cast(realDestVA), size, input, size); + ioctlHandler->munmap(mappedPtr, alignedSize); + } + } else { + size_t pendingSize = size; + uint8_t retry = 0; + const uint8_t maxRetries = 3; + size_t retrySize = size; + do { + PRINT_DEBUGGER_MEM_ACCESS_LOG("Writing (pwrite) memory to gpu va = %#" PRIx64 ", size = %zu\n", gpuVa, pendingSize); + retVal = ioctlHandler->pwrite(vmDebugFd, input, pendingSize, gpuVa); + input += retVal; + gpuVa += retVal; + pendingSize -= retVal; + if (retVal == 0) { + if (pendingSize < retrySize) { + retry = 0; + } + retry++; + retrySize = pendingSize; + } + } while (((retVal == 0) && (retry < maxRetries)) || ((retVal > 0) && (pendingSize > 0))); + + if (retVal < 0) { + PRINT_DEBUGGER_ERROR_LOG("Writing memory failed, errno = %d\n", errno); + } + + retVal = pendingSize; + } + + NEO::SysCalls::close(vmDebugFd); + + return (retVal == 0) ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_UNKNOWN; +} + +ze_result_t DebugSessionLinux::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; + } + + ze_result_t result = ZE_RESULT_NOT_READY; + + uint8_t maxEventBuffer[sizeof(prelim_drm_i915_debug_event) + maxEventSize]; + auto event = reinterpret_cast(maxEventBuffer); + + event->size = maxEventSize; + + bool allEventsCollected = false; + + do { + event->type = PRELIM_DRM_I915_DEBUG_EVENT_READ; + event->flags = 0; + event->size = maxEventSize; + + result = readEventImp(event); + + allEventsCollected = checkAllEventsCollected(); + + } while (result == ZE_RESULT_SUCCESS && !allEventsCollected); + + if (clientHandleClosed == clientHandle && clientHandle != invalidClientHandle) { + return ZE_RESULT_ERROR_DEVICE_LOST; + } + + if (allEventsCollected) { + if (!readModuleDebugArea()) { + return ZE_RESULT_ERROR_UNKNOWN; + } + return ZE_RESULT_SUCCESS; + } + + if (result != ZE_RESULT_SUCCESS) { + return result; + } + return ZE_RESULT_NOT_READY; +} + +void *DebugSessionLinux::asyncThread(void *arg) { + DebugSessionLinux *self = reinterpret_cast(arg); + PRINT_DEBUGGER_INFO_LOG("Debugger async thread start\n", ""); + + uint8_t maxEventBuffer[sizeof(prelim_drm_i915_debug_event) + maxEventSize]; + auto event = reinterpret_cast(maxEventBuffer); + + event->size = maxEventSize; + event->type = PRELIM_DRM_I915_DEBUG_EVENT_READ; + event->flags = 0; + + while (self->asyncThreadActive) { + self->handleEventsAsync(event); + + self->sendInterrupts(); + self->generateEventsAndResumeStoppedThreads(); + } + + PRINT_DEBUGGER_INFO_LOG("Debugger async thread closing\n", ""); + + self->asyncThreadFinished.store(true); + return nullptr; +} + +void DebugSessionLinux::startAsyncThread() { + thread = NEO::Thread::create(asyncThread, reinterpret_cast(this)); +} + +void DebugSessionLinux::closeAsyncThread() { + asyncThreadActive.store(false); + + if (thread) { + while (!asyncThreadFinished.load()) { + thread->yield(); + } + + thread->join(); + thread.reset(); + } +} + +void DebugSessionLinux::handleEventsAsync(prelim_drm_i915_debug_event *event) { + 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 (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, nullptr); + detached = true; + } else if (numberOfFds > 0) { + + ze_result_t result = ZE_RESULT_SUCCESS; + + int maxLoopCount = 3; + do { + event->type = PRELIM_DRM_I915_DEBUG_EVENT_READ; + event->flags = 0; + event->size = maxEventSize; + + result = readEventImp(event); + maxLoopCount--; + } while (result == ZE_RESULT_SUCCESS && maxLoopCount > 0); + } +} + +bool DebugSessionLinux::closeConnection() { + closeAsyncThread(); + + if (fd == 0) { + return false; + } + + auto res = NEO::SysCalls::close(fd); + + if (res != 0) { + PRINT_DEBUGGER_ERROR_LOG("Debug connection close() on fd: %d failed: retCode: %d\n", fd, res); + return false; + } + return true; +} + +void DebugSessionLinux::handleEvent(prelim_drm_i915_debug_event *event) { + auto type = event->type; + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type = %lu flags = %d seqno = %llu size = %llu", + (uint32_t)event->type, (int)event->flags, (uint64_t)event->seqno, (uint64_t)event->size); + + switch (type) { + case PRELIM_DRM_I915_DEBUG_EVENT_CLIENT: { + auto clientEvent = reinterpret_cast(event); + + if (event->flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE) { + DEBUG_BREAK_IF(clientHandleToConnection.find(clientEvent->handle) != clientHandleToConnection.end()); + clientHandleToConnection[clientEvent->handle].reset(new ClientConnection); + clientHandleToConnection[clientEvent->handle]->client = *clientEvent; + } + + if (event->flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY) { + clientHandleClosed = clientEvent->handle; + } + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_CLIENT flags = %d size = %llu client.handle = %llu\n", + (int)event->flags, (uint64_t)event->size, (uint64_t)clientEvent->handle); + + } break; + + case PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT: { + prelim_drm_i915_debug_event_context *context = reinterpret_cast(event); + + if (event->flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE) { + UNRECOVERABLE_IF(clientHandleToConnection.find(context->client_handle) == clientHandleToConnection.end()); + clientHandleToConnection[context->client_handle]->contextsCreated[context->handle].handle = context->handle; + } + + if (event->flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY) { + clientHandleToConnection[context->client_handle]->contextsCreated.erase(context->handle); + } + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT flags = %d size = %llu client_handle = %llu handle = %llu\n", + (int)event->flags, (uint64_t)event->size, (uint64_t)context->client_handle, (uint64_t)context->handle); + } break; + + case PRELIM_DRM_I915_DEBUG_EVENT_UUID: { + prelim_drm_i915_debug_event_uuid *uuid = reinterpret_cast(event); + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_UUID flags = %d size = %llu client_handle = %llu handle = %llu class_handle = %llu payload_size = %llu\n", + (int)event->flags, (uint64_t)event->size, (uint64_t)uuid->client_handle, (uint64_t)uuid->handle, (uint64_t)uuid->class_handle, (uint64_t)uuid->payload_size); + + bool destroy = event->flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + bool create = event->flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + + if (destroy && clientHandleToConnection[uuid->client_handle]->uuidMap[uuid->handle].classIndex == NEO::DrmResourceClass::L0ZebinModule) { + DEBUG_BREAK_IF(clientHandleToConnection[uuid->client_handle]->uuidToModule[uuid->handle].segmentVmBindCounter != 0 || + clientHandleToConnection[uuid->client_handle]->uuidToModule[uuid->handle].loadAddresses.size() > 0); + + clientHandleToConnection[uuid->client_handle]->uuidToModule.erase(uuid->handle); + } + + if (destroy && (uuidL0CommandQueueHandle == uuid->handle) && (clientHandle == uuid->client_handle)) { + zet_debug_event_t debugEvent = {}; + debugEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT; + pushApiEvent(debugEvent, nullptr); + break; + } + + if (create) { + const auto &connection = clientHandleToConnection[uuid->client_handle]; + if (uuid->payload_size) { + prelim_drm_i915_debug_read_uuid readUuid = {}; + auto payload = std::make_unique(uuid->payload_size); + readUuid.client_handle = uuid->client_handle; + readUuid.handle = static_cast(uuid->handle); + readUuid.payload_ptr = reinterpret_cast(payload.get()); + readUuid.payload_size = uuid->payload_size; + auto res = ioctl(PRELIM_I915_DEBUG_IOCTL_READ_UUID, &readUuid); + + if (res == 0) { + std::string uuidString = std::string(readUuid.uuid, 36); + uint32_t classIndex = static_cast(NEO::DrmResourceClass::MaxSize); + auto validClassUuid = NEO::DrmUuid::getClassUuidIndex(uuidString, classIndex); + + if (uuidString == NEO::uuidL0CommandQueueHash) { + if ((clientHandle == invalidClientHandle) || (clientHandle == uuid->client_handle)) { + clientHandle = uuid->client_handle; + uuidL0CommandQueueHandle = uuid->handle; + zet_debug_event_t debugEvent = {}; + debugEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY; + pushApiEvent(debugEvent, nullptr); + } + } + + if (validClassUuid) { + if (clientHandle == invalidClientHandle) { + clientHandle = uuid->client_handle; + } + std::string className(reinterpret_cast(readUuid.payload_ptr), readUuid.payload_size); + connection->classHandleToIndex[uuid->handle] = {className, classIndex}; + } else { + auto &uuidData = connection->uuidMap[uuid->handle]; + uuidData.classHandle = uuid->class_handle; + uuidData.handle = uuid->handle; + uuidData.data = std::move(payload); + uuidData.dataSize = uuid->payload_size; + uuidData.classIndex = NEO::DrmResourceClass::MaxSize; + + const auto indexIt = connection->classHandleToIndex.find(uuid->class_handle); + if (indexIt != connection->classHandleToIndex.end()) { + uuidData.classIndex = static_cast(indexIt->second.second); + } + + if (uuidData.classIndex == NEO::DrmResourceClass::Elf) { + auto cpuVa = extractVaFromUuidString(uuidString); + uuidData.ptr = cpuVa; + } + + if (uuidData.classIndex == NEO::DrmResourceClass::L0ZebinModule) { + uint64_t handle = uuid->handle; + + auto &newModule = connection->uuidToModule[handle]; + newModule.segmentCount = 0; + newModule.segmentVmBindCounter = 0; + } + extractUuidData(uuid->client_handle, uuidData); + } + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_UUID client_handle = %llu handle = %llu flags = %d uuid = %s res = %d\n", + (uint64_t)readUuid.client_handle, (uint64_t)readUuid.handle, (int)readUuid.flags, uuidString.c_str(), res); + } else { + PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_READ_UUID res = %d errno = %d\n", res, errno); + } + } else { + connection->uuidMap[uuid->handle].classHandle = uuid->class_handle; + connection->uuidMap[uuid->handle].handle = uuid->handle; + } + } + + } break; + + case PRELIM_DRM_I915_DEBUG_EVENT_VM: { + prelim_drm_i915_debug_event_vm *vm = reinterpret_cast(event); + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_VM flags = %d size = %llu client_handle = %llu handle = %llu\n", + (int)event->flags, (uint64_t)event->size, (uint64_t)vm->client_handle, (uint64_t)vm->handle); + + if (event->flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE) { + UNRECOVERABLE_IF(clientHandleToConnection.find(vm->client_handle) == clientHandleToConnection.end()); + clientHandleToConnection[vm->client_handle]->vmIds.emplace(static_cast(vm->handle)); + } + + if (event->flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY) { + UNRECOVERABLE_IF(clientHandleToConnection.find(vm->client_handle) == clientHandleToConnection.end()); + clientHandleToConnection[vm->client_handle]->vmIds.erase(static_cast(vm->handle)); + } + } break; + + case PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND: { + prelim_drm_i915_debug_event_vm_bind *vmBind = reinterpret_cast(event); + + handleVmBindEvent(vmBind); + + } break; + + case PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT_PARAM: { + prelim_drm_i915_debug_event_context_param *contextParam = reinterpret_cast(event); + handleContextParamEvent(contextParam); + } break; + + case PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION: { + prelim_drm_i915_debug_event_eu_attention *attention = reinterpret_cast(event); + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION flags = %d, seqno = %d, size = %llu" + " client_handle = %llu flags = %llu class = %lu instance = %lu bitmask_size = %lu ctx_handle = %llu\n", + (int)attention->base.flags, (uint64_t)attention->base.seqno, (uint64_t)attention->base.size, + (uint64_t)attention->client_handle, (uint64_t)attention->flags, (uint32_t)attention->ci.engine_class, + (uint32_t)attention->ci.engine_instance, (uint32_t)attention->bitmask_size, uint64_t(attention->ctx_handle)); + + handleAttentionEvent(attention); + } break; + + case PRELIM_DRM_I915_DEBUG_EVENT_ENGINES: { + prelim_drm_i915_debug_event_engines *engines = reinterpret_cast(event); + handleEnginesEvent(engines); + } break; + default: + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: UNHANDLED %d flags = %d size = %llu\n", (int)event->type, (int)event->flags, (uint64_t)event->size); + break; + } +} + +bool DebugSessionLinux::checkAllEventsCollected() { + bool allEventsCollected = false; + bool clientConnected = (this->clientHandle != invalidClientHandle); + if (clientConnected) { + if (clientHandleToConnection[clientHandle]->vmToModuleDebugAreaBindInfo.size() > 0) { + allEventsCollected = true; + } + } + return allEventsCollected; +} + +bool DebugSessionLinux::readModuleDebugArea() { + auto vm = clientHandleToConnection[clientHandle]->vmToModuleDebugAreaBindInfo.begin()->first; + auto gpuVa = clientHandleToConnection[clientHandle]->vmToModuleDebugAreaBindInfo.begin()->second.gpuVa; + + memset(this->debugArea.magic, 0, sizeof(this->debugArea.magic)); + auto retVal = readGpuMemory(vm, reinterpret_cast(&this->debugArea), sizeof(this->debugArea), gpuVa); + + if (retVal != ZE_RESULT_SUCCESS || strncmp(this->debugArea.magic, "dbgarea", sizeof(DebugAreaHeader::magic)) != 0) { + PRINT_DEBUGGER_ERROR_LOG("Reading Module Debug Area failed, error = %d\n", retVal); + return false; + } + + return true; +} + +void DebugSessionLinux::readStateSaveAreaHeader() { + if (clientHandle == invalidClientHandle) { + return; + } + + uint64_t vm = 0; + uint64_t gpuVa = 0; + size_t totalSize = 0; + + { + std::unique_lock lock(asyncThreadMutex); + if (clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.size() > 0) { + vm = clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.begin()->first; + gpuVa = clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.begin()->second.gpuVa; + totalSize = clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.begin()->second.size; + } + } + + if (gpuVa > 0) { + auto headerSize = sizeof(SIP::StateSaveAreaHeader); + + if (totalSize < headerSize) { + PRINT_DEBUGGER_ERROR_LOG("Context State Save Area size incorrect\n", ""); + return; + } + std::vector data(headerSize); + auto retVal = readGpuMemory(vm, data.data(), headerSize, gpuVa); + + if (retVal != 0) { + PRINT_DEBUGGER_ERROR_LOG("Reading Context State Save Area failed, error = %d\n", retVal); + } else { + auto pStateSaveArea = reinterpret_cast(data.data()); + if (0 == strcmp(pStateSaveArea->versionHeader.magic, "tssarea")) { + size_t size = pStateSaveArea->versionHeader.size * 8u; + DEBUG_BREAK_IF(size != sizeof(SIP::StateSaveAreaHeader)); + stateSaveAreaHeader.assign(data.begin(), data.begin() + size); + + PRINT_DEBUGGER_INFO_LOG("Context State Save Area : version == %d.%d.%d\n", (int)pStateSaveArea->versionHeader.version.major, (int)pStateSaveArea->versionHeader.version.minor, (int)pStateSaveArea->versionHeader.version.patch); + } + } + } +} + +ze_result_t DebugSessionLinux::readEvent(uint64_t timeout, zet_debug_event_t *outputEvent) { + + if (outputEvent) { + outputEvent->type = ZET_DEBUG_EVENT_TYPE_INVALID; + outputEvent->flags = 0; + } else { + return ZE_RESULT_ERROR_INVALID_NULL_POINTER; + } + + if (clientHandle == invalidClientHandle) { + return ZE_RESULT_ERROR_UNINITIALIZED; + } + + do { + std::unique_lock lock(asyncThreadMutex); + + if (timeout > 0 && clientHandleToConnection[clientHandle]->apiEvents.size() == 0) { + readEventCondition.wait_for(lock, std::chrono::milliseconds(timeout)); + } + + if (clientHandleToConnection[clientHandle]->apiEvents.size() > 0) { + *outputEvent = clientHandleToConnection[clientHandle]->apiEvents.front(); + clientHandleToConnection[clientHandle]->apiEvents.pop(); + return ZE_RESULT_SUCCESS; + } + } while (timeout == UINT64_MAX && asyncThreadActive); + + return ZE_RESULT_NOT_READY; +} + +ze_result_t DebugSessionLinux::readEventImp(prelim_drm_i915_debug_event *drmDebugEvent) { + auto res = ioctl(PRELIM_I915_DEBUG_IOCTL_READ_EVENT, drmDebugEvent); + + if (res != 0) { + PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT failed: retCode: %d errno = %d\n", res, errno); + } else { + if ((drmDebugEvent->flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE) == 0 && + (drmDebugEvent->flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY) == 0 && + (drmDebugEvent->flags & PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE) == 0) { + + PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT unsupported flag = %d\n", (int)drmDebugEvent->flags); + return ZE_RESULT_ERROR_UNKNOWN; + } + handleEvent(drmDebugEvent); + return ZE_RESULT_SUCCESS; + } + return ZE_RESULT_NOT_READY; +} + +void DebugSessionLinux::handleVmBindEvent(prelim_drm_i915_debug_event_vm_bind *vmBind) { + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND flags = %d size = %llu client_handle = %llu vm_handle = %llu va_start = %p va_lenght = %llu num_uuids = %lu\n", + (int)vmBind->base.flags, (uint64_t)vmBind->base.size, (uint64_t)vmBind->client_handle, (uint64_t)vmBind->vm_handle, (void *)vmBind->va_start, (uint64_t)vmBind->va_length, (uint32_t)vmBind->num_uuids); + + const bool createEvent = (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE); + const bool destroyEvent = (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY); + + bool shouldAckEvent = true; + + if (vmBind->num_uuids > 0 && vmBind->base.size > sizeof(prelim_drm_i915_debug_event_vm_bind)) { + auto vmHandle = vmBind->vm_handle; + uint32_t index = 0; + auto connection = clientHandleToConnection[vmBind->client_handle].get(); + auto uuid = vmBind->uuids[index]; + + if (connection->uuidMap.find(uuid) == connection->uuidMap.end()) { + PRINT_DEBUGGER_ERROR_LOG("Unknown UUID handle = %llu\n", (uint64_t)uuid); + return; + } + PRINT_DEBUGGER_INFO_LOG("UUID handle = %llu class index = %d\n", (uint64_t)vmBind->uuids[index], (int)clientHandleToConnection[vmBind->client_handle]->uuidMap[vmBind->uuids[index]].classIndex); + + auto classUuid = connection->uuidMap[uuid].classHandle; + + if (connection->classHandleToIndex.find(classUuid) != connection->classHandleToIndex.end()) { + + std::unique_lock lock(asyncThreadMutex); + + if (connection->classHandleToIndex[classUuid].second == + static_cast(NEO::DrmResourceClass::SbaTrackingBuffer)) { + connection->vmToStateBaseAreaBindInfo[vmHandle] = {vmBind->va_start, vmBind->va_length}; + } + + if (connection->classHandleToIndex[classUuid].second == + static_cast(NEO::DrmResourceClass::ModuleHeapDebugArea)) { + connection->vmToModuleDebugAreaBindInfo[vmHandle] = {vmBind->va_start, vmBind->va_length}; + } + + if (connection->classHandleToIndex[classUuid].second == + static_cast(NEO::DrmResourceClass::ContextSaveArea)) { + connection->vmToContextStateSaveAreaBindInfo[vmHandle] = {vmBind->va_start, vmBind->va_length}; + } + } + + if (connection->uuidMap[uuid].classIndex == NEO::DrmResourceClass::Isa) { + PRINT_DEBUGGER_INFO_LOG("ISA vm_handle = %llu", (uint64_t)vmHandle); + + bool perKernelModules = true; + int moduleUUIDindex = -1; + + for (uint32_t uuidIter = 1; uuidIter < vmBind->num_uuids; uuidIter++) { + if (connection->uuidMap[vmBind->uuids[uuidIter]].classIndex == NEO::DrmResourceClass::L0ZebinModule) { + perKernelModules = false; + moduleUUIDindex = static_cast(uuidIter); + } + } + + if (connection->isaMap.find(vmBind->va_start) == connection->isaMap.end() && createEvent) { + auto isaUuidHandle = connection->uuidMap[vmBind->uuids[index]].handle; + auto &isaMap = connection->isaMap; + auto &elfMap = connection->elfMap; + + auto loadAddress = vmBind->va_start; + + auto isa = std::make_unique(); + isa->bindInfo = {vmBind->va_start, vmBind->va_length}; + isa->vmHandle = vmHandle; + isa->elfUuidHandle = invalidHandle; + isa->moduleBegin = 0; + isa->moduleEnd = 0; + + for (index = 1; index < vmBind->num_uuids; index++) { + if (connection->uuidMap[vmBind->uuids[index]].classIndex == NEO::DrmResourceClass::Elf) { + isa->elfUuidHandle = vmBind->uuids[index]; + + if (!perKernelModules) { + auto &module = connection->uuidToModule[vmBind->uuids[moduleUUIDindex]]; + DEBUG_BREAK_IF(module.elfUuidHandle != 0 && connection->uuidMap[vmBind->uuids[index]].ptr != connection->uuidMap[module.elfUuidHandle].ptr); + + module.elfUuidHandle = vmBind->uuids[index]; + } + } + + if (connection->uuidMap[vmBind->uuids[index]].classHandle == isaUuidHandle) { + isa->cookies.emplace(static_cast(vmBind->uuids[index])); + } + } + + if (isa->elfUuidHandle != invalidHandle) { + isa->moduleBegin = connection->uuidMap[isa->elfUuidHandle].ptr; + isa->moduleEnd = isa->moduleBegin + connection->uuidMap[isa->elfUuidHandle].dataSize; + elfMap[isa->moduleBegin] = isa->elfUuidHandle; + } else { + PRINT_DEBUGGER_ERROR_LOG("No ELF provided by application\n", ""); + } + + zet_debug_event_t debugEvent = {}; + debugEvent.type = ZET_DEBUG_EVENT_TYPE_MODULE_LOAD; + debugEvent.info.module.format = ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF; + debugEvent.info.module.load = loadAddress; + debugEvent.info.module.moduleBegin = isa->moduleBegin; + debugEvent.info.module.moduleEnd = isa->moduleEnd; + + std::unique_lock memLock(asyncThreadMutex); + isaMap[vmBind->va_start] = std::move(isa); + memLock.unlock(); + + if (perKernelModules) { + pushApiEvent(debugEvent, &vmBind->base); + shouldAckEvent = false; + } + } + + if (createEvent) { + std::unique_lock lock(asyncThreadMutex); + if (!connection->isaMap[vmBind->va_start]->moduleLoadEventAck && perKernelModules) { + PRINT_DEBUGGER_INFO_LOG("Add event to ack, seqno = %llu", (uint64_t)vmBind->base.seqno); + connection->isaMap[vmBind->va_start]->ackEvents.push_back(vmBind->base); + shouldAckEvent = false; + } + + connection->isaMap[vmBind->va_start]->vmBindCounter++; + } + + if (destroyEvent && connection->isaMap.find(vmBind->va_start) != connection->isaMap.end()) { + DEBUG_BREAK_IF(connection->isaMap[vmBind->va_start]->vmBindCounter == 0); + connection->isaMap[vmBind->va_start]->vmBindCounter--; + if (connection->isaMap[vmBind->va_start]->vmBindCounter == 0) { + const auto &isa = connection->isaMap[vmBind->va_start]; + + zet_debug_event_t debugEvent = {}; + + debugEvent.type = ZET_DEBUG_EVENT_TYPE_MODULE_UNLOAD; + debugEvent.info.module.format = ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF; + debugEvent.info.module.load = isa->bindInfo.gpuVa; + debugEvent.info.module.moduleBegin = isa->moduleBegin; + debugEvent.info.module.moduleEnd = isa->moduleEnd; + + if (perKernelModules) { + pushApiEvent(debugEvent, nullptr); + } + std::unique_lock memLock(asyncThreadMutex); + connection->isaMap.erase(vmBind->va_start); + memLock.unlock(); + } + } + } + + for (uint32_t uuidIter = 0; uuidIter < vmBind->num_uuids; uuidIter++) { + if (connection->uuidMap[vmBind->uuids[uuidIter]].classIndex == NEO::DrmResourceClass::L0ZebinModule) { + uint64_t loadAddress = 0; + auto &module = connection->uuidToModule[vmBind->uuids[uuidIter]]; + + if (createEvent) { + module.segmentVmBindCounter++; + + DEBUG_BREAK_IF(module.loadAddresses.size() > module.segmentCount); + bool canTriggerEvent = module.loadAddresses.size() == (module.segmentCount - 1); + module.loadAddresses.insert(vmBind->va_start); + + if (canTriggerEvent && module.loadAddresses.size() == module.segmentCount) { + loadAddress = *std::min_element(module.loadAddresses.begin(), module.loadAddresses.end()); + PRINT_DEBUGGER_INFO_LOG("Zebin module loaded at: %p, with %u isa allocations", (void *)loadAddress, module.segmentCount); + + zet_debug_event_t debugEvent = {}; + debugEvent.type = ZET_DEBUG_EVENT_TYPE_MODULE_LOAD; + debugEvent.info.module.format = ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF; + debugEvent.info.module.load = loadAddress; + debugEvent.info.module.moduleBegin = connection->uuidMap[module.elfUuidHandle].ptr; + debugEvent.info.module.moduleEnd = connection->uuidMap[module.elfUuidHandle].ptr + connection->uuidMap[module.elfUuidHandle].dataSize; + + pushApiEvent(debugEvent, &vmBind->base); + shouldAckEvent = false; + } + } else { //destroyEvent + + module.segmentVmBindCounter--; + + if (module.segmentVmBindCounter == 0) { + + zet_debug_event_t debugEvent = {}; + + auto loadAddress = *std::min_element(module.loadAddresses.begin(), module.loadAddresses.end()); + debugEvent.type = ZET_DEBUG_EVENT_TYPE_MODULE_UNLOAD; + debugEvent.info.module.format = ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF; + debugEvent.info.module.load = loadAddress; + debugEvent.info.module.moduleBegin = connection->uuidMap[module.elfUuidHandle].ptr; + debugEvent.info.module.moduleEnd = connection->uuidMap[module.elfUuidHandle].ptr + connection->uuidMap[module.elfUuidHandle].dataSize; + + pushApiEvent(debugEvent, nullptr); + module.loadAddresses.clear(); + } + } + } + } + } + + if (shouldAckEvent && (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK)) { + prelim_drm_i915_debug_event_ack eventToAck = {}; + eventToAck.type = vmBind->base.type; + eventToAck.seqno = vmBind->base.seqno; + eventToAck.flags = 0; + auto ret = ioctl(PRELIM_I915_DEBUG_IOCTL_ACK_EVENT, &eventToAck); + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_ACK_EVENT seqno = %llu ret = %d errno = %d\n", (uint64_t)eventToAck.seqno, ret, ret != 0 ? errno : 0); + } +} + +void DebugSessionLinux::handleContextParamEvent(prelim_drm_i915_debug_event_context_param *contextParam) { + + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT_PARAM flags = %d size = %llu client_handle = %llu ctx_handle = %llu\n", + (int)contextParam->base.flags, (uint64_t)contextParam->base.size, (uint64_t)contextParam->client_handle, (uint64_t)contextParam->ctx_handle); + + if (clientHandleToConnection[contextParam->client_handle]->contextsCreated.find(contextParam->ctx_handle) == + clientHandleToConnection[contextParam->client_handle]->contextsCreated.end()) { + PRINT_DEBUGGER_ERROR_LOG("CONTEXT handle does not exist\n", ""); + return; + } + + switch (contextParam->param.param) { + case I915_CONTEXT_PARAM_VM: + PRINT_DEBUGGER_INFO_LOG("I915_CONTEXT_PARAM_VM vm = %llu\n", (uint64_t)contextParam->param.value); + clientHandleToConnection[contextParam->client_handle]->contextsCreated[contextParam->ctx_handle].vm = contextParam->param.value; + break; + case I915_CONTEXT_PARAM_ENGINES: { + PRINT_DEBUGGER_INFO_LOG("I915_CONTEXT_PARAM_ENGINES ctx_id = %lu param = %llu value = %llu size = %lu \n", + (uint32_t)contextParam->param.ctx_id, + (uint64_t)contextParam->param.param, + (uint64_t)contextParam->param.value, (uint32_t)contextParam->param.size); + + auto numEngines = (contextParam->param.size - sizeof(i915_context_param_engines)) / sizeof(i915_engine_class_instance); + auto engines = reinterpret_cast(&(contextParam->param.value)); + + clientHandleToConnection[contextParam->client_handle]->contextsCreated[contextParam->ctx_handle].engines.clear(); + + for (uint32_t i = 0; i < numEngines; i++) { + clientHandleToConnection[contextParam->client_handle]->contextsCreated[contextParam->ctx_handle].engines.push_back(engines->engines[i]); + } + break; + } + default: + PRINT_DEBUGGER_INFO_LOG("I915_CONTEXT_PARAM UNHANDLED = %llu\n", (uint64_t)contextParam->param.param); + break; + } +} + +void DebugSessionLinux::handleAttentionEvent(prelim_drm_i915_debug_event_eu_attention *attention) { + NEO::EngineClassInstance engineClassInstance = {attention->ci.engine_class, attention->ci.engine_instance}; + auto tileIndex = DrmHelper::getEngineTileIndex(connectedDevice, engineClassInstance); + if (interruptSent && attention->base.seqno <= euControlInterruptSeqno[tileIndex]) { + PRINT_DEBUGGER_INFO_LOG("Discarding EU ATTENTION event for interrupt request. Event seqno == %d <= %d == interrupt seqno\n", + (uint32_t)attention->base.seqno, + (uint32_t)euControlInterruptSeqno[tileIndex]); + return; + } + + newAttentionRaised(tileIndex); + + if (clientHandleToConnection.find(attention->client_handle) == clientHandleToConnection.end()) { + return; + } + + auto &clientConnection = clientHandleToConnection[attention->client_handle]; + if (clientConnection->lrcToContextHandle.find(attention->lrc_handle) == clientConnection->lrcToContextHandle.end()) { + return; + } + + auto contextHandle = clientConnection->lrcToContextHandle[attention->lrc_handle]; + if (clientConnection->contextsCreated.find(contextHandle) == clientConnection->contextsCreated.end()) { + return; + } + + auto vmHandle = clientConnection->contextsCreated[contextHandle].vm; + if (vmHandle == invalidHandle) { + return; + } + + auto hwInfo = connectedDevice->getHwInfo(); + auto &l0HwHelper = L0HwHelper::get(hwInfo.platform.eRenderCoreFamily); + + auto threadsWithAttention = l0HwHelper.getThreadsFromAttentionBitmask(hwInfo, attention->bitmask, attention->bitmask_size); + + printBitmask(attention->bitmask, attention->bitmask_size); + + PRINT_DEBUGGER_THREAD_LOG("ATTENTION for tile = %d thread count = %d\n", tileIndex, (int)threadsWithAttention.size()); + + for (auto &thread : threadsWithAttention) { + EuThread::ThreadId threadId = {tileIndex, thread.slice, thread.subslice, thread.eu, thread.thread}; + PRINT_DEBUGGER_THREAD_LOG("ATTENTION event for thread: %s\n", EuThread::toString(threadId).c_str()); + + markPendingInterruptsOrAddToNewlyStoppedFromRaisedAttention(threadId, vmHandle); + } + + checkTriggerEventsForAttention(); +} + +void DebugSessionLinux::handleEnginesEvent(prelim_drm_i915_debug_event_engines *engines) { + PRINT_DEBUGGER_INFO_LOG("ENGINES event: client_handle = %llu, ctx_handle = %llu, num_engines = %llu %s\n", + (uint64_t)engines->client_handle, + (uint64_t)engines->ctx_handle, + (uint64_t)engines->num_engines, + engines->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE ? "CREATE" : engines->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY ? "DESTROY" + : ""); + + UNRECOVERABLE_IF(clientHandleToConnection.find(engines->client_handle) == clientHandleToConnection.end()); + + if (engines->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_CREATE) { + for (uint64_t i = 0; i < engines->num_engines; ++i) { + auto lrc = engines->engines[i].lrc_handle; + if (lrc != 0) { + PRINT_DEBUGGER_INFO_LOG(" lrc%llu = %llu", i, lrc); + } + clientHandleToConnection[engines->client_handle]->lrcToContextHandle[lrc] = engines->ctx_handle; + } + } + + if (engines->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_DESTROY) { + for (uint64_t i = 0; i < engines->num_engines; ++i) { + auto lrc = engines->engines[i].lrc_handle; + PRINT_DEBUGGER_INFO_LOG(" lrc%llu = %llu\n", i, lrc); + clientHandleToConnection[engines->client_handle]->lrcToContextHandle.erase(lrc); + } + } +} + +void DebugSessionLinux::extractUuidData(uint64_t client, const UuidData &uuidData) { + if (uuidData.classIndex == NEO::DrmResourceClass::SbaTrackingBuffer || + uuidData.classIndex == NEO::DrmResourceClass::ModuleHeapDebugArea || + uuidData.classIndex == NEO::DrmResourceClass::ContextSaveArea) { + UNRECOVERABLE_IF(uuidData.dataSize != 8); + uint64_t *data = (uint64_t *)uuidData.data.get(); + + if (uuidData.classIndex == NEO::DrmResourceClass::SbaTrackingBuffer) { + clientHandleToConnection[client]->stateBaseAreaGpuVa = *data; + PRINT_DEBUGGER_INFO_LOG("SbaTrackingBuffer GPU VA = %p", (void *)clientHandleToConnection[clientHandle]->stateBaseAreaGpuVa); + } + if (uuidData.classIndex == NEO::DrmResourceClass::ModuleHeapDebugArea) { + clientHandleToConnection[client]->moduleDebugAreaGpuVa = *data; + PRINT_DEBUGGER_INFO_LOG("ModuleHeapDebugArea GPU VA = %p", (void *)clientHandleToConnection[clientHandle]->moduleDebugAreaGpuVa); + } + if (uuidData.classIndex == NEO::DrmResourceClass::ContextSaveArea) { + clientHandleToConnection[client]->contextStateSaveAreaGpuVa = *data; + PRINT_DEBUGGER_INFO_LOG("ContextSaveArea GPU VA = %p", (void *)clientHandleToConnection[clientHandle]->contextStateSaveAreaGpuVa); + } + } + + if (uuidData.classIndex == NEO::DrmResourceClass::L0ZebinModule) { + uint32_t segmentCount = 0; + memcpy_s(&segmentCount, sizeof(uint32_t), uuidData.data.get(), uuidData.dataSize); + clientHandleToConnection[client]->uuidToModule[uuidData.handle].segmentCount = segmentCount; + } +} + +uint64_t DebugSessionLinux::extractVaFromUuidString(std::string &uuid) { + const char uuidString[] = "%04" SCNx64 "-%012" SCNx64; + auto subString = uuid.substr(19); + + uint64_t parts[2] = {0, 0}; + sscanf(subString.c_str(), uuidString, &parts[1], &parts[0]); + + parts[0] |= (parts[1] & 0xFFFF) << 48; + return parts[0]; +} + +int DebugSessionLinux::threadControl(std::vector threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmaskOut, size_t &bitmaskSizeOut) { + + auto hwInfo = connectedDevice->getHwInfo(); + auto classInstance = DrmHelper::getEngineInstance(connectedDevice, tile, hwInfo.capabilityTable.defaultEngineType); + UNRECOVERABLE_IF(!classInstance); + + auto &l0HwHelper = L0HwHelper::get(hwInfo.platform.eRenderCoreFamily); + + bitmaskSizeOut = 0; + + struct prelim_drm_i915_debug_eu_control euControl = {}; + euControl.client_handle = clientHandle; + euControl.ci.engine_class = classInstance->engineClass; + euControl.ci.engine_instance = classInstance->engineInstance; + euControl.bitmask_size = 0; + euControl.bitmask_ptr = 0; + + decltype(prelim_drm_i915_debug_eu_control::cmd) command = 0; + switch (threadCmd) { + case ThreadControlCmd::InterruptAll: + command = PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT_ALL; + break; + case ThreadControlCmd::Interrupt: + command = PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT; + break; + case ThreadControlCmd::Resume: + command = PRELIM_I915_DEBUG_EU_THREADS_CMD_RESUME; + break; + case ThreadControlCmd::Stopped: + command = PRELIM_I915_DEBUG_EU_THREADS_CMD_STOPPED; + break; + } + euControl.cmd = command; + + std::unique_ptr bitmask; + size_t bitmaskSize = 0; + + if (command == PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT || + command == PRELIM_I915_DEBUG_EU_THREADS_CMD_RESUME || + command == PRELIM_I915_DEBUG_EU_THREADS_CMD_STOPPED) { + l0HwHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize); + euControl.bitmask_size = static_cast(bitmaskSize); + euControl.bitmask_ptr = reinterpret_cast(bitmask.get()); + } + + if (command == PRELIM_I915_DEBUG_EU_THREADS_CMD_RESUME) { + applyResumeWa(threads, bitmask.get(), bitmaskSize); + } + + printBitmask(bitmask.get(), bitmaskSize); + + auto res = ioctl(PRELIM_I915_DEBUG_IOCTL_EU_CONTROL, &euControl); + if (res != 0) { + PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_EU_CONTROL failed: retCode: %d errno = %d command = %d\n", res, errno, command); + } else { + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_EU_CONTROL: seqno = %llu command = %u\n", (uint64_t)euControl.seqno, command); + } + + if (command == PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT || + command == PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT_ALL) { + if (res == 0) { + euControlInterruptSeqno[tile] = euControl.seqno; + } else { + euControlInterruptSeqno[tile] = invalidHandle; + } + } + + if (threadCmd == ThreadControlCmd::Stopped) { + bitmaskOut = std::move(bitmask); + bitmaskSizeOut = euControl.bitmask_size; + } + return res; +} + +ze_result_t DebugSessionLinux::resumeImp(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; +} + +ze_result_t DebugSessionLinux::getISAVMHandle(const zet_debug_memory_space_desc_t *desc, size_t size, uint64_t &vmHandle) { + auto accessVA = desc->address; + auto &isaMap = clientHandleToConnection[clientHandle]->isaMap; + ze_result_t status = ZE_RESULT_ERROR_UNINITIALIZED; + vmHandle = invalidHandle; + + if (isaMap.size() > 0) { + uint64_t baseVa; + uint64_t ceilVa; + for (const auto &isa : isaMap) { + baseVa = isa.second->bindInfo.gpuVa; + ceilVa = isa.second->bindInfo.gpuVa + isa.second->bindInfo.size; + if (accessVA >= baseVa && accessVA < ceilVa) { + if (accessVA + size > ceilVa) { + status = ZE_RESULT_ERROR_INVALID_ARGUMENT; + } else { + vmHandle = isa.second->vmHandle; + status = ZE_RESULT_SUCCESS; + } + break; + } + } + } + + return status; +} + +void DebugSessionLinux::printContextVms() { + if (NEO::DebugManager.flags.DebuggerLogBitmask.get() & NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_INFO) { + PRINT_DEBUGGER_LOG(stdout, "\nINFO: Context - VM map: ", ""); + for (size_t i = 0; i < clientHandleToConnection[clientHandle]->contextsCreated.size(); i++) { + PRINT_DEBUGGER_LOG(stdout, "\n Context = %llu : %llu ", (uint64_t)clientHandleToConnection[clientHandle]->contextsCreated[i].handle, + (uint64_t)clientHandleToConnection[clientHandle]->contextsCreated[i].vm); + } + } +} + +ze_result_t DebugSessionLinux::getElfOffset(const zet_debug_memory_space_desc_t *desc, size_t size, const char *&elfData, uint64_t &offset) { + auto &elfMap = clientHandleToConnection[clientHandle]->elfMap; + auto accessVA = desc->address; + ze_result_t status = ZE_RESULT_ERROR_UNINITIALIZED; + elfData = nullptr; + + if (elfMap.size() > 0) { + uint64_t baseVa; + uint64_t ceilVa; + for (auto elf : elfMap) { + baseVa = elf.first; + ceilVa = elf.first + clientHandleToConnection[clientHandle]->uuidMap[elf.second].dataSize; + if (accessVA >= baseVa && accessVA < ceilVa) { + if (accessVA + size > ceilVa) { + status = ZE_RESULT_ERROR_INVALID_ARGUMENT; + } else { + DEBUG_BREAK_IF(clientHandleToConnection[clientHandle]->uuidMap[elf.second].data.get() == nullptr); + elfData = clientHandleToConnection[clientHandle]->uuidMap[elf.second].data.get(); + offset = accessVA - baseVa; + status = ZE_RESULT_SUCCESS; + } + break; + } + } + } + + return status; +} + +ze_result_t DebugSessionLinux::readElfSpace(const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer, + const char *&elfData, const uint64_t offset) { + + int retVal = -1; + elfData += offset; + retVal = memcpy_s(buffer, size, elfData, size); + return (retVal == 0) ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_UNKNOWN; +} + +ze_result_t DebugSessionLinux::readMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer) { + + if (clientHandle == invalidClientHandle) { + return ZE_RESULT_ERROR_UNINITIALIZED; + } + + if (!isValidGpuAddress(desc->address)) { + return ZE_RESULT_ERROR_INVALID_ARGUMENT; + } + + ze_result_t status = ZE_RESULT_ERROR_UNINITIALIZED; + status = sanityMemAccessThreadCheck(thread, desc); + if (status != ZE_RESULT_SUCCESS) { + return status; + } + + uint64_t vmHandle = invalidHandle; + std::vector allVms; + + { + std::unique_lock memLock(asyncThreadMutex); + + status = getISAVMHandle(desc, size, vmHandle); + if (status == ZE_RESULT_ERROR_INVALID_ARGUMENT) { + return status; + } + + if (vmHandle == invalidHandle) { + const char *elfData = nullptr; + uint64_t offset = 0; + status = getElfOffset(desc, size, elfData, offset); + if (status == ZE_RESULT_ERROR_INVALID_ARGUMENT) { + return status; + } + + if (elfData) { + return readElfSpace(desc, size, buffer, elfData, offset); + } + + auto &vmIds = clientHandleToConnection[clientHandle]->vmIds; + allVms.resize(vmIds.size()); + std::copy(vmIds.begin(), vmIds.end(), allVms.begin()); + } + } + + if (vmHandle == invalidHandle) { + if (DebugSession::isThreadAll(thread)) { + if (allVms.size() > 0) { + for (auto vmHandle : allVms) { + status = readGpuMemory(vmHandle, static_cast(buffer), size, desc->address); + if (status == ZE_RESULT_SUCCESS) { + return status; + } + } + } + + return ZE_RESULT_ERROR_UNINITIALIZED; + + } else { + auto threadId = convertToThreadId(thread); + vmHandle = allThreads[threadId]->getMemoryHandle(); + if (vmHandle == invalidHandle) { + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + } + } + + return readGpuMemory(vmHandle, static_cast(buffer), size, desc->address); +} + +ze_result_t DebugSessionLinux::writeMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, const void *buffer) { + + if (clientHandle == invalidClientHandle) { + return ZE_RESULT_ERROR_UNINITIALIZED; + } + + if (!isValidGpuAddress(desc->address)) { + return ZE_RESULT_ERROR_INVALID_ARGUMENT; + } + + ze_result_t status = ZE_RESULT_ERROR_UNINITIALIZED; + status = sanityMemAccessThreadCheck(thread, desc); + if (status != ZE_RESULT_SUCCESS) { + return status; + } + + uint64_t vmHandle = invalidHandle; + std::vector allVms; + + { + std::unique_lock memLock(asyncThreadMutex); + + status = getISAVMHandle(desc, size, vmHandle); + if (status == ZE_RESULT_ERROR_INVALID_ARGUMENT) { + return status; + } + + if (vmHandle == invalidHandle) { + auto &vmIds = clientHandleToConnection[clientHandle]->vmIds; + allVms.resize(vmIds.size()); + std::copy(vmIds.begin(), vmIds.end(), allVms.begin()); + } + } + + if (vmHandle == invalidHandle) { + if (DebugSession::isThreadAll(thread)) { + if (allVms.size() > 0) { + for (auto vmHandle : allVms) { + status = writeGpuMemory(vmHandle, static_cast(buffer), size, desc->address); + if (status == ZE_RESULT_SUCCESS) { + return status; + } + } + } + + return ZE_RESULT_ERROR_UNINITIALIZED; + + } else { + auto threadId = convertToThreadId(thread); + vmHandle = allThreads[threadId]->getMemoryHandle(); + if (vmHandle == invalidHandle) { + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + } + } + + return writeGpuMemory(vmHandle, static_cast(buffer), size, desc->address); +} + +ze_result_t DebugSessionLinux::acknowledgeEvent(const zet_debug_event_t *event) { + std::unique_lock lock(asyncThreadMutex); + for (size_t i = 0; i < clientHandleToConnection[clientHandle]->eventsToAck.size(); i++) { + if (apiEventCompare(*event, clientHandleToConnection[clientHandle]->eventsToAck[i].first)) { + + bool perKernelIsaAcked = false; + if (event->type == ZET_DEBUG_EVENT_TYPE_MODULE_LOAD) { + auto connection = clientHandleToConnection[clientHandle].get(); + auto isa = connection->isaMap.find(event->info.module.load); + + if (isa != connection->isaMap.end()) { + for (auto &event : isa->second->ackEvents) { + prelim_drm_i915_debug_event_ack eventToAck = {}; + eventToAck.type = event.type; + eventToAck.seqno = event.seqno; + eventToAck.flags = 0; + + auto ret = ioctl(PRELIM_I915_DEBUG_IOCTL_ACK_EVENT, &eventToAck); + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_ACK_EVENT seqno = %llu, ret = %d errno = %d\n", (uint64_t)event.seqno, ret, ret != 0 ? errno : 0); + + perKernelIsaAcked = true; + } + isa->second->ackEvents.clear(); + isa->second->moduleLoadEventAck = true; + } + } + + if (!perKernelIsaAcked) { + auto eventToAck = clientHandleToConnection[clientHandle]->eventsToAck[i].second; + auto ret = ioctl(PRELIM_I915_DEBUG_IOCTL_ACK_EVENT, &eventToAck); + PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_ACK_EVENT seqno = %llu, ret = %d errno = %d\n", (uint64_t)eventToAck.seqno, ret, ret != 0 ? errno : 0); + } + + auto iter = clientHandleToConnection[clientHandle]->eventsToAck.begin() + i; + clientHandleToConnection[clientHandle]->eventsToAck.erase(iter); + + return ZE_RESULT_SUCCESS; + } + } + return ZE_RESULT_ERROR_UNKNOWN; +} + +bool DebugSessionLinux::readSystemRoutineIdent(EuThread *thread, uint64_t vmHandle, SIP::sr_ident &srIdent) { + auto stateSaveAreaHeader = getStateSaveAreaHeader(); + if (!stateSaveAreaHeader) { + return false; + } + + auto gpuVa = getContextStateSaveAreaGpuVa(vmHandle); + if (gpuVa == 0) { + return false; + } + auto threadSlotOffset = calculateThreadSlotOffset(thread->getThreadId()); + auto srMagicOffset = threadSlotOffset + getStateSaveAreaHeader()->regHeader.sr_magic_offset; + + if (ZE_RESULT_SUCCESS != readGpuMemory(vmHandle, reinterpret_cast(&srIdent), sizeof(srIdent), gpuVa + srMagicOffset)) { + return false; + } + return true; +} + +ze_result_t DebugSessionLinux::readSbaBuffer(EuThread::ThreadId threadId, SbaTrackedAddresses &sbaBuffer) { + auto vmHandle = allThreads[threadId]->getMemoryHandle(); + + if (vmHandle == invalidHandle) { + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + + auto gpuVa = getSbaBufferGpuVa(vmHandle); + if (gpuVa == 0) { + return ZE_RESULT_ERROR_UNKNOWN; + } + + return readGpuMemory(vmHandle, reinterpret_cast(&sbaBuffer), sizeof(sbaBuffer), gpuVa); +} + +uint64_t DebugSessionLinux::getSbaBufferGpuVa(uint64_t memoryHandle) { + std::unique_lock lock(asyncThreadMutex); + auto bindInfo = clientHandleToConnection[clientHandle]->vmToStateBaseAreaBindInfo.find(memoryHandle); + if (bindInfo == clientHandleToConnection[clientHandle]->vmToStateBaseAreaBindInfo.end()) { + return 0; + } + + return bindInfo->second.gpuVa; +} + +uint64_t DebugSessionLinux::getContextStateSaveAreaGpuVa(uint64_t memoryHandle) { + std::unique_lock lock(asyncThreadMutex); + auto bindInfo = clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.find(memoryHandle); + if (bindInfo == clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.end()) { + return 0; + } + + return bindInfo->second.gpuVa; +} + +void DebugSessionLinux::applyResumeWa(std::vector threads, uint8_t *bitmask, size_t bitmaskSize) { + + UNRECOVERABLE_IF(bitmaskSize % 8 != 0); + + auto hwInfo = connectedDevice->getHwInfo(); + auto &l0HwHelper = L0HwHelper::get(hwInfo.platform.eRenderCoreFamily); + + if (l0HwHelper.isResumeWARequired()) { + + uint32_t *dwordBitmask = reinterpret_cast(bitmask); + for (uint32_t i = 0; i < bitmaskSize / sizeof(uint32_t) - 1; i = i + 2) { + dwordBitmask[i] = dwordBitmask[i] | dwordBitmask[i + 1]; + dwordBitmask[i + 1] = dwordBitmask[i] | dwordBitmask[i + 1]; + } + } + return; +} + +ze_device_thread_t DebugSessionLinux::convertToPhysical(ze_device_thread_t thread, uint32_t &deviceIndex) { + auto deviceImp = static_cast(connectedDevice); + + if (thread.slice != UINT32_MAX) { + if (thread.subslice != UINT32_MAX) { + deviceImp->toPhysicalSliceId(DrmHelper::getTopologyMap(connectedDevice), thread.slice, thread.subslice, deviceIndex); + } else { + uint32_t dummy = 0; + deviceImp->toPhysicalSliceId(DrmHelper::getTopologyMap(connectedDevice), thread.slice, dummy, deviceIndex); + } + } + + return thread; +} + +EuThread::ThreadId DebugSessionLinux::convertToThreadId(ze_device_thread_t thread) { + auto deviceImp = static_cast(connectedDevice); + UNRECOVERABLE_IF(!DebugSession::isSingleThread(thread)); + + uint32_t deviceIndex = 0; + deviceImp->toPhysicalSliceId(DrmHelper::getTopologyMap(connectedDevice), thread.slice, thread.subslice, deviceIndex); + + EuThread::ThreadId threadId(deviceIndex, thread.slice, thread.subslice, thread.eu, thread.thread); + return threadId; +} + +ze_device_thread_t DebugSessionLinux::convertToApi(EuThread::ThreadId threadId) { + auto deviceImp = static_cast(connectedDevice); + + ze_device_thread_t thread = {static_cast(threadId.slice), static_cast(threadId.subslice), static_cast(threadId.eu), static_cast(threadId.thread)}; + deviceImp->toApiSliceId(DrmHelper::getTopologyMap(connectedDevice), thread.slice, thread.subslice, threadId.tileIndex); + + return thread; +} +} // namespace L0 diff --git a/level_zero/tools/source/debug/linux/prelim/debug_session.h b/level_zero/tools/source/debug/linux/prelim/debug_session.h new file mode 100644 index 0000000000..d70d26c907 --- /dev/null +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2022 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once +#include "shared/source/helpers/topology_map.h" +#include "shared/source/os_interface/linux/drm_debug.h" +#include "shared/source/os_interface/linux/sys_calls.h" + +#include "level_zero/core/source/device/device.h" +#include "level_zero/tools/source/debug/debug_session.h" +#include "level_zero/tools/source/debug/debug_session_imp.h" + +#include "third_party/uapi/prelim/drm/i915_drm.h" + +#include +#include +#include +#include +#include + +namespace NEO { +class Thread; +struct EngineClassInstance; +} // namespace NEO + +namespace L0 { + +struct DebugSessionLinux : DebugSessionImp { + ~DebugSessionLinux() override; + DebugSessionLinux(const zet_debug_config_t &config, Device *device, int debugFd); + + ze_result_t initialize() override; + + bool closeConnection() override; + ze_result_t readEvent(uint64_t timeout, zet_debug_event_t *event) override; + ze_result_t readMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer) override; + 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; + + ze_device_thread_t convertToPhysical(ze_device_thread_t thread, uint32_t &deviceIndex) override; + EuThread::ThreadId convertToThreadId(ze_device_thread_t thread) override; + ze_device_thread_t convertToApi(EuThread::ThreadId threadId) override; + + struct IoctlHandler { + MOCKABLE_VIRTUAL ~IoctlHandler() = default; + MOCKABLE_VIRTUAL int ioctl(int fd, unsigned long request, void *arg) { + int ret = 0; + do { + ret = NEO::SysCalls::ioctl(fd, request, arg); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EBUSY)); + 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; + + using ContextHandle = uint64_t; + using ApiEventQueue = std::queue; + + struct ContextParams { + ContextHandle handle = 0; + uint64_t vm = UINT64_MAX; + std::vector engines; + }; + + struct UuidData { + uint64_t handle = 0; + uint64_t classHandle = 0; + NEO::DrmResourceClass classIndex = NEO::DrmResourceClass::MaxSize; + std::unique_ptr data; + size_t dataSize = 0; + + uint64_t ptr = 0; + }; + + struct BindInfo { + uint64_t gpuVa = 0; + uint64_t size = 0; + }; + + struct IsaAllocation { + BindInfo bindInfo; + uint64_t elfUuidHandle; + uint64_t vmHandle; + + uint64_t moduleBegin; + uint64_t moduleEnd; + + std::unordered_set cookies; + int vmBindCounter = 0; + bool moduleLoadEventAck = false; + std::vector ackEvents; + }; + + struct Module { + std::unordered_set loadAddresses; + uint64_t elfUuidHandle; + uint32_t segmentCount; + int segmentVmBindCounter; + }; + + static bool apiEventCompare(const zet_debug_event_t &event1, const zet_debug_event_t &event2) { + return memcmp(&event1, &event2, sizeof(zet_debug_event_t)) == 0; + }; + + struct ClientConnection { + prelim_drm_i915_debug_event_client client = {}; + + std::unordered_map contextsCreated; + std::unordered_map> classHandleToIndex; + std::unordered_map uuidMap; + std::unordered_set vmIds; + + std::unordered_map vmToModuleDebugAreaBindInfo; + std::unordered_map vmToContextStateSaveAreaBindInfo; + std::unordered_map vmToStateBaseAreaBindInfo; + + std::unordered_map> isaMap; + std::unordered_map elfMap; + std::unordered_map lrcToContextHandle; + + uint64_t moduleDebugAreaGpuVa = 0; + uint64_t contextStateSaveAreaGpuVa = 0; + uint64_t stateBaseAreaGpuVa = 0; + + ApiEventQueue apiEvents; + std::vector> eventsToAck; + + std::unordered_map uuidToModule; + }; + + static ze_result_t translateDebuggerOpenErrno(int error); + constexpr static uint64_t invalidClientHandle = std::numeric_limits::max(); + constexpr static uint64_t invalidHandle = std::numeric_limits::max(); + + protected: + enum class ThreadControlCmd { + Interrupt, + Resume, + Stopped, + InterruptAll + }; + MOCKABLE_VIRTUAL void handleEvent(prelim_drm_i915_debug_event *event); + bool checkAllEventsCollected(); + ze_result_t readEventImp(prelim_drm_i915_debug_event *drmDebugEvent); + ze_result_t resumeImp(std::vector threads, uint32_t deviceIndex) override; + ze_result_t interruptImp(uint32_t deviceIndex) override; + + void enqueueApiEvent(zet_debug_event_t &debugEvent) override { + pushApiEvent(debugEvent, nullptr); + } + + void pushApiEvent(zet_debug_event_t &debugEvent, prelim_drm_i915_debug_event *baseEvent) { + std::unique_lock lock(asyncThreadMutex); + + if (baseEvent && (baseEvent->flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK)) { + prelim_drm_i915_debug_event_ack eventToAck = {}; + eventToAck.type = baseEvent->type; + eventToAck.seqno = baseEvent->seqno; + eventToAck.flags = 0; + debugEvent.flags = ZET_DEBUG_EVENT_FLAG_NEED_ACK; + + clientHandleToConnection[clientHandle]->eventsToAck.push_back( + std::pair(debugEvent, eventToAck)); + } + + clientHandleToConnection[clientHandle]->apiEvents.push(debugEvent); + + readEventCondition.notify_all(); + } + + static void *asyncThread(void *arg); + void startAsyncThread() override; + void closeAsyncThread(); + void handleEventsAsync(prelim_drm_i915_debug_event *event); + + void handleVmBindEvent(prelim_drm_i915_debug_event_vm_bind *vmBind); + void handleContextParamEvent(prelim_drm_i915_debug_event_context_param *contextParam); + void handleAttentionEvent(prelim_drm_i915_debug_event_eu_attention *attention); + void handleEnginesEvent(prelim_drm_i915_debug_event_engines *engines); + + void extractUuidData(uint64_t client, const UuidData &uuidData); + uint64_t extractVaFromUuidString(std::string &uuid); + + bool readModuleDebugArea() override; + ze_result_t readSbaBuffer(EuThread::ThreadId, SbaTrackedAddresses &sbaBuffer) override; + void readStateSaveAreaHeader() override; + + void applyResumeWa(std::vector threads, uint8_t *bitmask, size_t bitmaskSize); + + ze_result_t readGpuMemory(uint64_t vmHandle, char *output, size_t size, uint64_t gpuVa) override; + ze_result_t writeGpuMemory(uint64_t vmHandle, const char *input, size_t size, uint64_t gpuVa) override; + ze_result_t getISAVMHandle(const zet_debug_memory_space_desc_t *desc, size_t size, uint64_t &vmHandle); + ze_result_t getElfOffset(const zet_debug_memory_space_desc_t *desc, size_t size, const char *&elfData, uint64_t &offset); + ze_result_t readElfSpace(const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer, + const char *&elfData, const uint64_t offset); + + bool readSystemRoutineIdent(EuThread *thread, uint64_t vmHandle, SIP::sr_ident &srIdent) override; + + MOCKABLE_VIRTUAL int threadControl(std::vector threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmask, size_t &bitmaskSize); + + uint64_t getContextStateSaveAreaGpuVa(uint64_t memoryHandle) override; + uint64_t getSbaBufferGpuVa(uint64_t memoryHandle); + void printContextVms(); + + std::atomic asyncThreadActive{true}; + std::atomic asyncThreadFinished{false}; + std::mutex asyncThreadMutex; + std::condition_variable readEventCondition; + std::unique_ptr thread; + + int fd = 0; + int ioctl(unsigned long request, void *arg); + std::unique_ptr ioctlHandler; + std::atomic detached{false}; + + uint64_t clientHandle = invalidClientHandle; + uint64_t clientHandleClosed = invalidClientHandle; + uint64_t uuidL0CommandQueueHandle = invalidClientHandle; + uint64_t euControlInterruptSeqno[NEO::EngineLimits::maxHandleCount]; + + std::unordered_map> clientHandleToConnection; +}; + +} // namespace L0 diff --git a/level_zero/tools/source/debug/linux/prelim/debug_session_linux_helper.cpp b/level_zero/tools/source/debug/linux/prelim/debug_session_linux_helper.cpp new file mode 100644 index 0000000000..f93f8f3365 --- /dev/null +++ b/level_zero/tools/source/debug/linux/prelim/debug_session_linux_helper.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2022 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/tools/source/debug/linux/prelim/debug_session.h" +#include +namespace L0 { + +DebugSession *createDebugSessionHelper(const zet_debug_config_t &config, Device *device, int debugFd) { + return new DebugSessionLinux(config, device, debugFd); +} + +} // namespace L0 diff --git a/level_zero/tools/source/debug/linux/prelim/drm_helper.cpp b/level_zero/tools/source/debug/linux/prelim/drm_helper.cpp new file mode 100644 index 0000000000..49245f3a0d --- /dev/null +++ b/level_zero/tools/source/debug/linux/prelim/drm_helper.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/tools/source/debug/linux/prelim/drm_helper.h" + +#include "shared/source/os_interface/linux/drm_neo.h" + +namespace L0 { + +int DrmHelper::ioctl(Device *device, unsigned long request, void *arg) { + auto drm = device->getOsInterface().getDriverModel()->as(); + return drm->ioctl(request, arg); +} + +int DrmHelper::getErrno(Device *device) { + auto drm = device->getOsInterface().getDriverModel()->as(); + return drm->getErrno(); +} + +uint32_t DrmHelper::getEngineTileIndex(Device *device, const NEO::EngineClassInstance &engine) { + auto drm = device->getOsInterface().getDriverModel()->as(); + auto engineInfo = drm->getEngineInfo(); + return engineInfo->getEngineTileIndex(engine); +} + +const NEO::EngineClassInstance *DrmHelper::getEngineInstance(Device *device, uint32_t tile, aub_stream::EngineType engineType) { + auto drm = device->getOsInterface().getDriverModel()->as(); + auto engineInfo = drm->getEngineInfo(); + return engineInfo->getEngineInstance(tile, engineType); +} + +const NEO::TopologyMap &DrmHelper::getTopologyMap(Device *device) { + auto drm = device->getOsInterface().getDriverModel()->as(); + return drm->getTopologyMap(); +} + +} // namespace L0 diff --git a/level_zero/tools/source/debug/linux/prelim/drm_helper.h b/level_zero/tools/source/debug/linux/prelim/drm_helper.h new file mode 100644 index 0000000000..f2a1c8d1ea --- /dev/null +++ b/level_zero/tools/source/debug/linux/prelim/drm_helper.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/helpers/topology_map.h" + +#include "level_zero/core/source/device/device.h" + +namespace NEO { +struct EngineClassInstance; +} + +namespace L0 { +struct DrmHelper { + static int ioctl(Device *device, unsigned long request, void *arg); + static int getErrno(Device *device); + static uint32_t getEngineTileIndex(Device *device, const NEO::EngineClassInstance &engine); + static const NEO::EngineClassInstance *getEngineInstance(Device *device, uint32_t tile, aub_stream::EngineType engineType); + static const NEO::TopologyMap &getTopologyMap(Device *device); +}; + +} // namespace L0 diff --git a/level_zero/tools/test/unit_tests/CMakeLists.txt b/level_zero/tools/test/unit_tests/CMakeLists.txt index 0b1a2adddd..0a84fa5d6b 100644 --- a/level_zero/tools/test/unit_tests/CMakeLists.txt +++ b/level_zero/tools/test/unit_tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2020-2021 Intel Corporation +# Copyright (C) 2020-2022 Intel Corporation # # SPDX-License-Identifier: MIT # @@ -46,6 +46,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER ${TARGET_NAME_L0}) add_subdirectoriesL0(${CMAKE_CURRENT_SOURCE_DIR} "*") target_compile_definitions(${TARGET_NAME} PRIVATE $) +target_include_directories(${TARGET_NAME} PRIVATE $) target_include_directories(${TARGET_NAME} BEFORE 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 new file mode 100644 index 0000000000..0509646f82 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright (C) 2022 Intel Corporation +# +# SPDX-License-Identifier: MIT +# + +if(UNIX) + if(NEO_ENABLE_i915_PRELIM_DETECTION) + target_sources(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${CMAKE_CURRENT_SOURCE_DIR}/test_debug_api_linux.cpp + ) + endif() +endif() diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_api_linux.cpp b/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_api_linux.cpp new file mode 100644 index 0000000000..473dc89aa0 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_api_linux.cpp @@ -0,0 +1,5955 @@ +/* + * Copyright (C) 2022 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +// Force prelim headers over upstream headers +// clang-format off +#include "third_party/uapi/prelim/drm/i915_drm.h" +// clang-format on + +// prevent including any other headers to avoid redefintion errors +#define _I915_DRM_H_ +#define _UAPI_I915_DRM_H_ + +#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/libult/linux/drm_query_mock.h" +#include "shared/test/common/mocks/mock_sip.h" +#include "shared/test/common/test_macros/test.h" + +#include "level_zero/core/source/hw_helpers/l0_hw_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/include/zet_intel_gpu_debug.h" +#include "level_zero/tools/source/debug/debug_handlers.h" +#include "level_zero/tools/source/debug/linux/prelim/debug_session.h" +#include "level_zero/tools/test/unit_tests/sources/debug/mock_debug_session.h" + +#include "common/StateSaveAreaHeader.h" + +#include +#include +#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 { + +using typeOfUUID = std::decay::type; + +struct MockIoctlHandler : public L0::DebugSessionLinux::IoctlHandler { + using EventPair = std::pair; + using EventQueue = std::queue; + + int ioctl(int fd, unsigned long request, void *arg) override { + ioctlCalled++; + + if ((request == PRELIM_I915_DEBUG_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; + } else if ((request == PRELIM_I915_DEBUG_IOCTL_ACK_EVENT) && (arg != nullptr)) { + auto debugEvent = reinterpret_cast(arg); + debugEventAcked = *debugEvent; + return 0; + } else if ((request == PRELIM_I915_DEBUG_IOCTL_READ_UUID) && (arg != nullptr)) { + prelim_drm_i915_debug_read_uuid *uuid = reinterpret_cast(arg); + if (returnUuid) { + uuid->client_handle = returnUuid->client_handle; + uuid->handle = returnUuid->handle; + uuid->flags = returnUuid->flags; + memcpy(uuid->uuid, returnUuid->uuid, sizeof(prelim_drm_i915_debug_read_uuid::uuid)); + int returnError = 0; + if (uuid->payload_size >= returnUuid->payload_size) { + memcpy(reinterpret_cast(uuid->payload_ptr), reinterpret_cast(returnUuid->payload_ptr), returnUuid->payload_size); + } else { + returnError = -1; + } + returnUuid = nullptr; + return returnError; + } + return -1; + } else if ((request == PRELIM_I915_DEBUG_IOCTL_VM_OPEN) && (arg != nullptr)) { + prelim_drm_i915_debug_vm_open *vmOpenIn = reinterpret_cast(arg); + vmOpen = *vmOpenIn; + return vmOpenRetVal; + } else if ((request == PRELIM_I915_DEBUG_IOCTL_EU_CONTROL) && (arg != nullptr)) { + prelim_drm_i915_debug_eu_control *euControlArg = reinterpret_cast(arg); + euControl = *euControlArg; + + euControlArg->seqno = euControlOutputSeqno; + + if (euControlArg->bitmask_size != 0) { + euControlBitmaskSize = euControlArg->bitmask_size; + euControlBitmask = std::make_unique(euControlBitmaskSize); + + memcpy(euControlBitmask.get(), reinterpret_cast(euControlArg->bitmask_ptr), euControlBitmaskSize); + } + } + + return ioctlRetVal; + } + + int poll(pollfd *pollFd, unsigned long int numberOfFds, int timeout) override { + passedTimeout = timeout; + pollCounter++; + return pollRetVal; + } + + int64_t pread(int fd, void *buf, size_t count, off_t offset) override { + preadCalled++; + preadOffset = offset; + if ((midZeroReturn > 0) && (preadCalled > 1)) { + midZeroReturn--; + return 0; + } + + if (!pReadArrayRef.empty()) { + auto offsetInMemory = offset - pReadBase; + auto result = memcpy_s(buf, count, pReadArrayRef.begin() + offsetInMemory, std::min(count, pReadArrayRef.size() - offsetInMemory)); + if (result == 0) { + return count; + } + return -1; + } + + if (count > 0 && preadRetVal > 0) { + memset(buf, 0xaa, count); + } + return preadRetVal; + } + + int64_t pwrite(int fd, const void *buf, size_t count, off_t offset) override { + pwriteCalled++; + pwriteOffset = offset; + if ((midZeroReturn > 0) && (pwriteCalled > 1)) { + midZeroReturn--; + return 0; + } + + if (!pWriteArrayRef.empty()) { + auto offsetInMemory = offset - pWriteBase; + auto result = memcpy_s(pWriteArrayRef.begin() + offsetInMemory, pWriteArrayRef.size() - offsetInMemory, buf, count); + if (result == 0) { + return count; + } + return -1; + } + + return pwriteRetVal; + } + + void *mmap(void *addr, size_t size, int prot, int flags, int fd, off_t off) override { + mmapCalled++; + if (mmapFail) { + return MAP_FAILED; + } + if (mmapRet) { + return mmapRet + (off - mmapBase); + } else { + auto ret = new char[size]; + if (size > 0) { + memset(ret, 0xaa, size); + } + return ret; + } + } + + int munmap(void *addr, size_t size) override { + munmapCalled++; + if (mmapRet) { + return 0; + } + if (addr != MAP_FAILED && addr != 0) { + if (*static_cast(addr) != static_cast(0xaa)) { + memoryModifiedInMunmap = true; + } + delete[](char *) addr; + } else { + return -1; + } + return 0; + } + + void setPreadMemory(char *memory, size_t size, uint64_t baseAddress) { + pReadArrayRef = ArrayRef(memory, size); + pReadBase = baseAddress; + } + + void setPwriteMemory(char *memory, size_t size, uint64_t baseAddress) { + pWriteArrayRef = ArrayRef(memory, size); + pWriteBase = baseAddress; + } + + prelim_drm_i915_debug_event debugEventInput = {}; + prelim_drm_i915_debug_event debugEventAcked = {}; + prelim_drm_i915_debug_read_uuid *returnUuid = nullptr; + prelim_drm_i915_debug_vm_open vmOpen = {}; + prelim_drm_i915_debug_eu_control euControl = {}; + + std::unique_ptr euControlBitmask; + size_t euControlBitmaskSize = 0; + + int ioctlRetVal = 0; + int debugEventRetVal = 0; + int ioctlCalled = 0; + int pollRetVal = 0; + int vmOpenRetVal = 600; + int passedTimeout = 0; + + ArrayRef pReadArrayRef; + uint64_t pReadBase = 0; + int64_t preadCalled = 0; + int64_t preadRetVal = 0; + + ArrayRef pWriteArrayRef; + uint64_t pWriteBase = 0; + int64_t pwriteCalled = 0; + int64_t pwriteRetVal = 0; + + uint64_t preadOffset = 0; + uint64_t pwriteOffset = 0; + + uint8_t midZeroReturn = 0; + int64_t mmapCalled = 0; + int64_t munmapCalled = 0; + bool mmapFail = false; + char *mmapRet = nullptr; + uint64_t mmapBase = 0; + bool memoryModifiedInMunmap = false; + std::atomic pollCounter = 0; + EventQueue eventQueue; + uint64_t euControlOutputSeqno = 10; +}; + +struct MockDebugSessionLinux : public L0::DebugSessionLinux { + using L0::DebugSessionImp::allThreads; + using L0::DebugSessionImp::enqueueApiEvent; + using L0::DebugSessionImp::expectedAttentionEvents; + using L0::DebugSessionImp::interruptSent; + using L0::DebugSessionImp::isValidGpuAddress; + using L0::DebugSessionImp::stateSaveAreaHeader; + using L0::DebugSessionImp::triggerEvents; + + using L0::DebugSessionLinux::asyncThreadActive; + using L0::DebugSessionLinux::asyncThreadFinished; + using L0::DebugSessionLinux::checkAllEventsCollected; + using L0::DebugSessionLinux::clientHandle; + using L0::DebugSessionLinux::clientHandleClosed; + using L0::DebugSessionLinux::clientHandleToConnection; + using L0::DebugSessionLinux::closeAsyncThread; + using L0::DebugSessionLinux::debugArea; + using L0::DebugSessionLinux::euControlInterruptSeqno; + using L0::DebugSessionLinux::extractVaFromUuidString; + using L0::DebugSessionLinux::getRegisterSetProperties; + using L0::DebugSessionLinux::getStateSaveAreaHeader; + using L0::DebugSessionLinux::handleEvent; + using L0::DebugSessionLinux::handleEventsAsync; + using L0::DebugSessionLinux::handleVmBindEvent; + using L0::DebugSessionLinux::interruptImp; + using L0::DebugSessionLinux::ioctl; + using L0::DebugSessionLinux::ioctlHandler; + using L0::DebugSessionLinux::newlyStoppedThreads; + using L0::DebugSessionLinux::pendingInterrupts; + using L0::DebugSessionLinux::printContextVms; + using L0::DebugSessionLinux::pushApiEvent; + using L0::DebugSessionLinux::readEventImp; + using L0::DebugSessionLinux::readGpuMemory; + using L0::DebugSessionLinux::readModuleDebugArea; + using L0::DebugSessionLinux::readSbaBuffer; + using L0::DebugSessionLinux::readStateSaveAreaHeader; + using L0::DebugSessionLinux::startAsyncThread; + using L0::DebugSessionLinux::thread; + using L0::DebugSessionLinux::threadControl; + using L0::DebugSessionLinux::ThreadControlCmd; + using L0::DebugSessionLinux::typeToRegsetDesc; + using L0::DebugSessionLinux::typeToRegsetFlags; + using L0::DebugSessionLinux::uuidL0CommandQueueHandle; + using L0::DebugSessionLinux::writeGpuMemory; + + MockDebugSessionLinux(const zet_debug_config_t &config, L0::Device *device, int debugFd) : DebugSessionLinux(config, device, debugFd) { + clientHandleToConnection[mockClientHandle].reset(new ClientConnection); + clientHandle = mockClientHandle; + } + + std::unordered_map> &getClassHandleToIndex() { + return clientHandleToConnection[mockClientHandle]->classHandleToIndex; + } + + int64_t getTimeDifferenceMilliseconds(std::chrono::high_resolution_clock::time_point time) override { + if (returnTimeDiff != -1) { + return returnTimeDiff; + } + return L0::DebugSessionLinux::getTimeDifferenceMilliseconds(time); + } + + int threadControl(std::vector threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmask, size_t &bitmaskSize) override { + numThreadsPassedToThreadControl = threads.size(); + return L0::DebugSessionLinux::threadControl(threads, tile, threadCmd, bitmask, bitmaskSize); + } + + ze_result_t readRegisters(ze_device_thread_t thread, uint32_t type, uint32_t start, uint32_t count, void *pRegisterValues) override { + readRegistersCallCount++; + return L0::DebugSessionLinux::readRegisters(thread, type, start, count, pRegisterValues); + } + + ze_result_t writeRegisters(ze_device_thread_t thread, uint32_t type, uint32_t start, uint32_t count, void *pRegisterValues) override { + writeRegistersCallCount++; + writeRegistersReg = type; + return L0::DebugSessionLinux::writeRegisters(thread, type, start, count, pRegisterValues); + } + + void handleEvent(prelim_drm_i915_debug_event *event) override { + handleEventCalledCount++; + L0::DebugSessionLinux::handleEvent(event); + } + + bool areRequestedThreadsStopped(ze_device_thread_t thread) override { + return allThreadsStopped; + } + + void ensureThreadStopped(ze_device_thread_t thread) { + auto threadId = convertToThreadId(thread); + if (allThreads.find(threadId) == allThreads.end()) { + allThreads[threadId] = std::make_unique(threadId); + } + allThreads[threadId]->stopThread(vmHandle); + } + + bool readSystemRoutineIdent(EuThread *thread, uint64_t vmHandle, SIP::sr_ident &srIdent) override { + srIdent.count = 0; + if (stoppedThreads.size()) { + auto entry = stoppedThreads.find(thread->getThreadId()); + if (entry != stoppedThreads.end()) { + srIdent.count = entry->second; + } + return true; + } + return L0::DebugSessionLinux::readSystemRoutineIdent(thread, vmHandle, srIdent); + } + + bool writeResumeCommand(const std::vector &threadIds) override { + writeResumeCommandCalled++; + if (skipWriteResumeCommand) { + return true; + } + return L0::DebugSessionLinux::writeResumeCommand(threadIds); + } + + bool checkThreadIsResumed(const EuThread::ThreadId &threadID) override { + checkThreadIsResumedCalled++; + if (skipcheckThreadIsResumed) { + return true; + } + return L0::DebugSessionLinux::checkThreadIsResumed(threadID); + } + + bool allThreadsStopped = false; + int64_t returnTimeDiff = -1; + static constexpr uint64_t mockClientHandle = 1; + size_t numThreadsPassedToThreadControl = 0; + uint32_t readRegistersCallCount = 0; + uint32_t writeRegistersCallCount = 0; + uint32_t writeRegistersReg = 0; + uint32_t handleEventCalledCount = 0; + uint64_t vmHandle = UINT64_MAX; + bool skipWriteResumeCommand = true; + uint32_t writeResumeCommandCalled = 0; + bool skipcheckThreadIsResumed = true; + uint32_t checkThreadIsResumedCalled = 0; + + std::unordered_map stoppedThreads; +}; + +size_t threadSlotOffset(SIP::StateSaveAreaHeader *pStateSaveAreaHeader, int slice, int subslice, int eu, int thread) { + return pStateSaveAreaHeader->versionHeader.size * 8 + + pStateSaveAreaHeader->regHeader.state_area_offset + + ((((slice * pStateSaveAreaHeader->regHeader.num_subslices_per_slice + subslice) * pStateSaveAreaHeader->regHeader.num_eus_per_subslice + eu) * pStateSaveAreaHeader->regHeader.num_threads_per_eu + thread) * pStateSaveAreaHeader->regHeader.state_save_size); +}; + +size_t regOffsetInThreadSlot(const SIP::regset_desc *regdesc, uint32_t start) { + return regdesc->offset + regdesc->bytes * start; +}; + +void initStateSaveArea(std::vector &stateSaveArea, SIP::version version) { + auto stateSaveAreaHeader = MockSipData::createStateSaveAreaHeader(version.major); + auto pStateSaveAreaHeader = reinterpret_cast(stateSaveAreaHeader.data()); + + if (version.major >= 2) { + stateSaveArea.resize( + threadSlotOffset(pStateSaveAreaHeader, 0, 0, 0, 0) + + pStateSaveAreaHeader->regHeader.num_subslices_per_slice * pStateSaveAreaHeader->regHeader.num_eus_per_subslice * pStateSaveAreaHeader->regHeader.num_threads_per_eu * pStateSaveAreaHeader->regHeader.state_save_size); + } else { + auto &hwInfo = *NEO::defaultHwInfo.get(); + auto &hwHelper = HwHelper::get(hwInfo.platform.eRenderCoreFamily); + stateSaveArea.resize(hwHelper.getSipKernelMaxDbgSurfaceSize(hwInfo) + MemoryConstants::pageSize); + } + + memcpy(stateSaveArea.data(), pStateSaveAreaHeader, sizeof(*pStateSaveAreaHeader)); + + auto fillRegForThread = [&](const SIP::regset_desc *regdesc, int slice, int subslice, int eu, int thread, int start, char value) { + memset(stateSaveArea.data() + threadSlotOffset(pStateSaveAreaHeader, slice, subslice, eu, thread) + regOffsetInThreadSlot(regdesc, start), value, regdesc->bytes); + }; + + auto fillRegsetForThread = [&](const SIP::regset_desc *regdesc, int slice, int subslice, int eu, int thread, char value) { + for (uint32_t reg = 0; reg < regdesc->num; ++reg) { + fillRegForThread(regdesc, slice, subslice, eu, thread, reg, value + reg); + } + SIP::sr_ident *srIdent = reinterpret_cast(stateSaveArea.data() + threadSlotOffset(pStateSaveAreaHeader, slice, subslice, eu, thread) + pStateSaveAreaHeader->regHeader.sr_magic_offset); + srIdent->count = 2; + srIdent->version.major = version.major; + srIdent->version.minor = version.minor; + srIdent->version.patch = version.patch; + strcpy(stateSaveArea.data() + threadSlotOffset(pStateSaveAreaHeader, slice, subslice, eu, thread) + pStateSaveAreaHeader->regHeader.sr_magic_offset, "srmagic"); // NOLINT(clang-analyzer-security.insecureAPI.strcpy) + }; + + // grfs for 0/0/0/0 - very first eu thread + fillRegsetForThread(&pStateSaveAreaHeader->regHeader.grf, 0, 0, 0, 0, 'a'); + + if (version.major < 2) { + // grfs for 0/3/7/3 - somewhere in the middle + fillRegsetForThread(&pStateSaveAreaHeader->regHeader.grf, 0, 3, 7, 3, 'a'); + + // grfs for 0/5/15/6 - very last eu thread + fillRegsetForThread(&pStateSaveAreaHeader->regHeader.grf, 0, 5, 15, 6, 'a'); + } +} + +struct DebugApiLinuxFixture : public DeviceFixture { + void SetUp() { + SetUp(nullptr); + } + + void SetUp(NEO::HardwareInfo *hwInfo) { + if (hwInfo != nullptr) { + auto executionEnvironment = MockDevice::prepareExecutionEnvironment(hwInfo, 0u); + DeviceFixture::setupWithExecutionEnvironment(*executionEnvironment); + } else { + DeviceFixture::SetUp(); + } + + mockDrm = new DrmQueryMock(*neoDevice->executionEnvironment->rootDeviceEnvironments[0]); + mockDrm->allowDebugAttach = true; + mockDrm->queryEngineInfo(); + + // set config from HwInfo to have correct topology requested by tests + if (hwInfo) { + mockDrm->storedSVal = hwInfo->gtSystemInfo.SliceCount; + mockDrm->storedSSVal = hwInfo->gtSystemInfo.SubSliceCount; + mockDrm->storedEUVal = hwInfo->gtSystemInfo.EUCount; + } + NEO::Drm::QueryTopologyData topologyData = {}; + mockDrm->queryTopology(neoDevice->getHardwareInfo(), topologyData); + + neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface); + neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::unique_ptr(mockDrm)); + } + + void TearDown() { + DeviceFixture::TearDown(); + } + DrmQueryMock *mockDrm = nullptr; + static constexpr uint8_t bufferSize = 16; +}; + +TEST(IoctlHandler, 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(IoctlHandler, 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(IoctlHandler, 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; +} + +TEST(DebugSessionTest, GivenDebugSessionWhenExtractingCpuVaFromUuidThenCorrectCpuVaReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, nullptr, 10); + + std::string uuid = "00000000-0000-0000-0000-5500044f4000"; + auto va = sessionMock->extractVaFromUuidString(uuid); + uint64_t epxectedVa = 0x00005500044f4000; + + EXPECT_EQ(epxectedVa, va); + + uuid = "00000000-0000-0000-00ff-5500044f4000"; + va = sessionMock->extractVaFromUuidString(uuid); + epxectedVa = 0x00ff5500044f4000; + + EXPECT_EQ(epxectedVa, va); +} + +TEST(DebugSessionTest, WhenConvertingThreadIdsThenDeviceFunctionsAreCalled) { + auto hwInfo = *NEO::defaultHwInfo.get(); + NEO::MockDevice *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + + auto mockDrm = new DrmQueryMock(*neoDevice->executionEnvironment->rootDeviceEnvironments[0]); + neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface); + neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::unique_ptr(mockDrm)); + + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + + auto sessionMock = std::make_unique(zet_debug_config_t{0x1234}, &deviceImp, 10); + ASSERT_NE(nullptr, sessionMock); + + ze_device_thread_t thread = {0, 0, 0, 0}; + + auto threadID = sessionMock->convertToThreadId(thread); + + EXPECT_EQ(0u, threadID.tileIndex); + EXPECT_EQ(0u, threadID.slice); + EXPECT_EQ(0u, threadID.subslice); + EXPECT_EQ(0u, threadID.eu); + EXPECT_EQ(0u, threadID.thread); + + auto apiThread = sessionMock->convertToApi(threadID); + + EXPECT_EQ(0u, apiThread.slice); + EXPECT_EQ(0u, apiThread.subslice); + EXPECT_EQ(0u, apiThread.eu); + EXPECT_EQ(0u, apiThread.thread); + + uint32_t deviceIndex = 1; + + auto physicalThread = sessionMock->convertToPhysical(thread, deviceIndex); + + EXPECT_EQ(1u, deviceIndex); + EXPECT_EQ(0u, physicalThread.slice); + EXPECT_EQ(0u, physicalThread.subslice); + EXPECT_EQ(0u, physicalThread.eu); + EXPECT_EQ(0u, physicalThread.thread); + + thread.slice = UINT32_MAX; + physicalThread = sessionMock->convertToPhysical(thread, deviceIndex); + + EXPECT_EQ(1u, deviceIndex); + EXPECT_EQ(uint32_t(UINT32_MAX), physicalThread.slice); + EXPECT_EQ(0u, physicalThread.subslice); + EXPECT_EQ(0u, physicalThread.eu); + EXPECT_EQ(0u, physicalThread.thread); + + thread.slice = 0; + thread.subslice = UINT32_MAX; + thread.eu = 1; + thread.thread = 3; + physicalThread = sessionMock->convertToPhysical(thread, deviceIndex); + + EXPECT_EQ(1u, deviceIndex); + EXPECT_EQ(0u, physicalThread.slice); + EXPECT_EQ(uint32_t(UINT32_MAX), physicalThread.subslice); + EXPECT_EQ(1u, physicalThread.eu); + EXPECT_EQ(3u, physicalThread.thread); +} + +TEST(DebugSessionTest, WhenEnqueueApiEventCalledThenEventPushed) { + auto sessionMock = std::make_unique(zet_debug_config_t{0x1234}, nullptr, 10); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + zet_debug_event_t debugEvent = {}; + + sessionMock->enqueueApiEvent(debugEvent); + + EXPECT_EQ(1u, sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); +} + +TEST(DebugSessionTest, GivenLogsEnabledWhenPrintContextVmsCalledThenMapIsPrinted) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(255); + + auto sessionMock = std::make_unique(zet_debug_config_t{0x1234}, nullptr, 10); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + DebugSessionLinux::ContextParams param = {}; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[0] = param; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[1] = param; + + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[0].handle = 0; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[1].handle = 1; + + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[0].vm = 1; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[1].vm = 2; + + EXPECT_EQ(2u, sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated.size()); + + ::testing::internal::CaptureStdout(); + + sessionMock->printContextVms(); + + auto map = ::testing::internal::GetCapturedStdout(); + + EXPECT_THAT(map, testing::HasSubstr(std::string("INFO: Context - VM map:"))); + EXPECT_THAT(map, testing::HasSubstr(std::string("Context = 0 : 1"))); + EXPECT_THAT(map, testing::HasSubstr(std::string("Context = 1 : 2"))); +} + +TEST(DebugSessionTest, GivenLogsDisabledWhenPrintContextVmsCalledThenMapIsiNotPrinted) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(0); + + auto sessionMock = std::make_unique(zet_debug_config_t{0x1234}, nullptr, 10); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + DebugSessionLinux::ContextParams param = {}; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[0] = param; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[1] = param; + + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[0].handle = 0; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[1].handle = 1; + + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[0].vm = 1; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated[1].vm = 2; + + EXPECT_EQ(2u, sessionMock->clientHandleToConnection[sessionMock->clientHandle]->contextsCreated.size()); + + ::testing::internal::CaptureStdout(); + + sessionMock->printContextVms(); + + auto map = ::testing::internal::GetCapturedStdout(); + + EXPECT_TRUE(map.empty()); +} + +TEST(DebugSessionTest, GivenNullptrEventWhenReadingEventThenErrorNullptrReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, nullptr, 10); + ASSERT_NE(nullptr, session); + + auto result = session->readEvent(10, nullptr); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_NULL_POINTER, result); +} + +using DebugApiLinuxTest = Test; + +TEST_F(DebugApiLinuxTest, 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(DebugApiLinuxTest, 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(DebugApiLinuxTest, WhenCallingResumeThenProperIoctlsAreCalled) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + SIP::version version = {2, 0, 0}; + initStateSaveArea(sessionMock->stateSaveAreaHeader, version); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + sessionMock->clientHandleToConnection[sessionMock->clientHandle]->vmToContextStateSaveAreaBindInfo[1u] = {0x1000, 0x1000}; + + zet_debug_session_handle_t session = sessionMock->toHandle(); + ze_device_thread_t thread = {}; + + sessionMock->allThreads[EuThread::ThreadId(0, thread)]->stopThread(1u); + + auto result = L0::DebugApiHandlers::debugResume(session, thread); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_RESUME), handler->euControl.cmd); +} + +TEST_F(DebugApiLinuxTest, GivenUnknownEventWhenAcknowledgeEventCalledThenErrorUnknownIsReturned) { + auto sessionMock = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, sessionMock); + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + + zet_debug_session_handle_t session = sessionMock->toHandle(); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + zet_debug_event_t debugEvent = {}; + + // No events to acknowledge + auto result = L0::DebugApiHandlers::debugAcknowledgeEvent(session, &debugEvent); + EXPECT_EQ(result, ZE_RESULT_ERROR_UNKNOWN); + + // One event to acknowledge + prelim_drm_i915_debug_event eventToAck = {}; + eventToAck.type = 500; + eventToAck.seqno = 5; + eventToAck.flags = PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK; + eventToAck.size = sizeof(prelim_drm_i915_debug_event); + + debugEvent.type = ZET_DEBUG_EVENT_TYPE_MODULE_LOAD; + debugEvent.info.module.format = ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF; + debugEvent.info.module.load = 0x1000; + debugEvent.info.module.moduleBegin = 0x2000; + debugEvent.info.module.moduleEnd = 0x3000; + + sessionMock->pushApiEvent(debugEvent, &eventToAck); + + // Different event acknowledged + debugEvent.info.module.load = 0x2221000; + debugEvent.info.module.moduleBegin = 0x2222000; + debugEvent.info.module.moduleEnd = 0x2223000; + + result = L0::DebugApiHandlers::debugAcknowledgeEvent(session, &debugEvent); + EXPECT_EQ(result, ZE_RESULT_ERROR_UNKNOWN); + + EXPECT_EQ(0, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenEventRequiringAckWhenAcknowledgeEventCalledThenSuccessIsReturned) { + auto sessionMock = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + + zet_debug_session_handle_t session = sessionMock->toHandle(); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + zet_debug_event_t debugEvent = {}; + debugEvent.flags = ZET_DEBUG_EVENT_FLAG_NEED_ACK; + + prelim_drm_i915_debug_event eventToAck = {}; + eventToAck.type = 500; + eventToAck.seqno = 10; + eventToAck.flags = PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK; + eventToAck.size = sizeof(prelim_drm_i915_debug_event); + sessionMock->pushApiEvent(debugEvent, &eventToAck); + + auto result = zetDebugAcknowledgeEvent(session, &debugEvent); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + EXPECT_EQ(500u, handler->debugEventAcked.type); + EXPECT_EQ(0u, handler->debugEventAcked.flags); + EXPECT_EQ(10u, handler->debugEventAcked.seqno); +} + +TEST_F(DebugApiLinuxTest, GivenSuccessfulInitializationWhenCreatingDebugSessionThenAsyncThreadIsStarted) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + ze_result_t result = ZE_RESULT_SUCCESS; + + mockDrm->context.debuggerOpenRetval = 10; + mockDrm->baseErrno = false; + mockDrm->errnoRetVal = 0; + + auto session = std::unique_ptr(DebugSession::create(config, device, result)); + + EXPECT_NE(nullptr, session); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + auto mockSession = static_cast(session.get()); + EXPECT_TRUE(mockSession->asyncThreadStarted); +} + +TEST_F(DebugApiLinuxTest, GivenClientAndMatchingUuidEventsWhenReadingEventsThenProcessEntryIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + prelim_drm_i915_debug_event_client clientCreate = {}; + clientCreate.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + clientCreate.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + clientCreate.base.size = sizeof(prelim_drm_i915_debug_event_client); + clientCreate.handle = MockDebugSessionLinux::mockClientHandle; + + auto uuidName = NEO::uuidL0CommandQueueName; + auto uuidNameSize = strlen(uuidName); + auto uuidHash = NEO::uuidL0CommandQueueHash; + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + uuid.handle = 2; + uuid.payload_size = uuidNameSize; + + prelim_drm_i915_debug_read_uuid readUuid = {}; + readUuid.client_handle = MockDebugSessionLinux::mockClientHandle; + memcpy(readUuid.uuid, uuidHash, strlen(uuidHash)); + readUuid.payload_ptr = reinterpret_cast(uuidName); + readUuid.payload_size = uuid.payload_size; + readUuid.handle = 3; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&clientCreate), static_cast(clientCreate.base.size)}); + handler->eventQueue.push({reinterpret_cast(&uuid), static_cast(uuid.base.size)}); + handler->pollRetVal = 1; + handler->returnUuid = &readUuid; + + session->ioctlHandler.reset(handler); + session->initialize(); + + handler->pollRetVal = 1; + + zet_debug_event_t event = {}; + ze_result_t result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY, event.type); +} + +TEST_F(DebugApiLinuxTest, GivenValidClassNameUuidWhenHandlingEventThenClientHandleIsSaved) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + auto uuidName = NEO::classNamesToUuid[0].first; + auto uuidNameSize = strlen(uuidName); + auto uuidHash = NEO::classNamesToUuid[0].second; + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + uuid.handle = 2; + uuid.payload_size = uuidNameSize; + + prelim_drm_i915_debug_read_uuid readUuid = {}; + memcpy(readUuid.uuid, uuidHash.data(), uuidHash.size()); + readUuid.payload_ptr = reinterpret_cast(uuidName); + readUuid.payload_size = uuid.payload_size; + readUuid.handle = 3; + + auto handler = new MockIoctlHandler; + handler->returnUuid = &readUuid; + session->ioctlHandler.reset(handler); + + session->handleEvent(&uuid.base); + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, session->clientHandle); + EXPECT_EQ(1, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenUuidCommandQueueCreatedHandledThenProcessEntryEventIsGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + auto uuidName = NEO::uuidL0CommandQueueName; + auto uuidNameSize = strlen(uuidName); + auto uuidHash = "InvalidUuidL0CommandQueueHash"; + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = 0; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + uuid.handle = 2; + uuid.payload_size = uuidNameSize; + + prelim_drm_i915_debug_read_uuid readUuid = {}; + memcpy(readUuid.uuid, uuidHash, strlen(uuidHash)); + readUuid.payload_ptr = reinterpret_cast(uuidName); + readUuid.payload_size = uuid.payload_size; + readUuid.handle = 3; + + auto handler = new MockIoctlHandler; + handler->returnUuid = &readUuid; + session->ioctlHandler.reset(handler); + + session->handleEvent(&uuid.base); + EXPECT_EQ(DebugSessionLinux::invalidClientHandle, session->clientHandle); + EXPECT_EQ(0, handler->ioctlCalled); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + handler->returnUuid = &readUuid; + session->clientHandle = 2; + session->handleEvent(&uuid.base); + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + + uuidHash = NEO::uuidL0CommandQueueHash; + memcpy(readUuid.uuid, uuidHash, strlen(uuidHash)); + handler->returnUuid = &readUuid; + session->handleEvent(&uuid.base); + EXPECT_EQ(2u, session->clientHandle); + EXPECT_EQ(2, handler->ioctlCalled); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + + session->clientHandle = DebugSessionLinux::invalidClientHandle; + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + handler->returnUuid = &readUuid; + session->handleEvent(&uuid.base); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, session->clientHandle); + EXPECT_EQ(3, handler->ioctlCalled); + auto event = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.front(); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY, event.type); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + handler->returnUuid = &readUuid; + session->handleEvent(&uuid.base); + EXPECT_EQ(2u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, session->clientHandle); + EXPECT_EQ(4, handler->ioctlCalled); + event = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.front(); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY, event.type); +} + +TEST_F(DebugApiLinuxTest, GivenCommandQueueDestroyedWhenHandlingEventThenExitEventIsGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidHandle; + session->uuidL0CommandQueueHandle = 5; + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = 0; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = 10; + uuid.handle = 4; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandleToConnection[10u].reset(new L0::DebugSessionLinux::ClientConnection); + + session->handleEvent(&uuid.base); + EXPECT_EQ(0u, session->clientHandleToConnection[10]->apiEvents.size()); + + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + session->handleEvent(&uuid.base); + EXPECT_EQ(0u, session->clientHandleToConnection[10]->apiEvents.size()); + + uuid.handle = session->uuidL0CommandQueueHandle; + session->handleEvent(&uuid.base); + EXPECT_EQ(0u, session->clientHandleToConnection[10]->apiEvents.size()); + + session->clientHandle = uuid.client_handle; + session->handleEvent(&uuid.base); + EXPECT_EQ(1u, session->clientHandleToConnection[10]->apiEvents.size()); + auto event = session->clientHandleToConnection[10]->apiEvents.front(); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT, event.type); +} + +TEST_F(DebugApiLinuxTest, GivenDestroyClientForClientNotSavedWhenHandlingEventThenNoEventIsGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = 1; + + prelim_drm_i915_debug_event_client clientDestroy = {}; + + clientDestroy.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + clientDestroy.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + clientDestroy.base.size = sizeof(prelim_drm_i915_debug_event_client); + clientDestroy.handle = 10; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandleToConnection[10u].reset(new L0::DebugSessionLinux::ClientConnection); + + session->handleEvent(&clientDestroy.base); + EXPECT_EQ(0u, session->clientHandleToConnection[10]->apiEvents.size()); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWhenReadingEventThenResultNotReadyIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + auto mockSession = new MockDebugSessionLinux(config, &deviceImp, 10); + mockSession->clientHandle = MockDebugSessionLinux::mockClientHandle; + deviceImp.debugSession.reset(mockSession); + + zet_debug_session_handle_t session = deviceImp.debugSession->toHandle(); + + zet_debug_event_t event = {}; + auto result = L0::DebugApiHandlers::debugReadEvent(session, 0, &event); + EXPECT_EQ(result, ZE_RESULT_NOT_READY); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWithoutValidClientHandleWhenReadingEventThenErrorNotInitializedIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + auto sessionMock = new MockDebugSessionLinux(config, &deviceImp, 10); + deviceImp.debugSession.reset(sessionMock); + + sessionMock->clientHandle = MockDebugSessionLinux::invalidClientHandle; + + zet_debug_session_handle_t session = deviceImp.debugSession->toHandle(); + + zet_debug_event_t event = {}; + auto result = L0::DebugApiHandlers::debugReadEvent(session, 0, &event); + EXPECT_EQ(result, ZE_RESULT_ERROR_UNINITIALIZED); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerLogsWhenOpenDebuggerFailsThenCorrectMessageIsPrintedAndResultSet) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(255); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + ze_result_t result = ZE_RESULT_SUCCESS; + + mockDrm->context.debuggerOpenRetval = -1; + mockDrm->baseErrno = false; + mockDrm->errnoRetVal = 22; + ::testing::internal::CaptureStderr(); + + auto session = DebugSession::create(config, device, result); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); + + auto errorMessage = ::testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string("\nERROR: PRELIM_DRM_IOCTL_I915_DEBUGGER_OPEN failed: open.pid: 4660, open.events: 0, retCode: -1, errno: 22\n"), errorMessage); +} + +TEST_F(DebugApiLinuxTest, WhenOpenDebuggerFailsThenCorrectErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + ze_result_t result = ZE_RESULT_SUCCESS; + + mockDrm->context.debuggerOpenRetval = -1; + mockDrm->baseErrno = false; + mockDrm->errnoRetVal = EBUSY; + auto session = DebugSession::create(config, device, result); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, result); + + mockDrm->errnoRetVal = ENODEV; + session = DebugSession::create(config, device, result); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, result); + + mockDrm->errnoRetVal = EACCES; + session = DebugSession::create(config, device, result); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS, result); + + mockDrm->errnoRetVal = ESRCH; + session = DebugSession::create(config, device, result); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerLogsWhenOpenDebuggerSucceedsThenCorrectMessageIsPrintedAndResultSet) { + DebugManagerStateRestore restorer; + + NEO::DebugManager.flags.DebuggerLogBitmask.set(255); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + ze_result_t result = ZE_RESULT_SUCCESS; + + mockDrm->context.debuggerOpenRetval = 10; + mockDrm->baseErrno = false; + mockDrm->errnoRetVal = 0; + ::testing::internal::CaptureStdout(); + + auto session = std::unique_ptr(DebugSession::create(config, device, result)); + + EXPECT_NE(nullptr, session); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + auto errorMessage = ::testing::internal::GetCapturedStdout(); + EXPECT_EQ(std::string("\nINFO: PRELIM_DRM_IOCTL_I915_DEBUGGER_OPEN: open.pid: 4660, open.events: 0, debugFd: 10\n"), errorMessage); +} + +TEST_F(DebugApiLinuxTest, 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; + auto ret = session->closeConnection(); + EXPECT_TRUE(ret); + + EXPECT_EQ(1u, NEO::SysCalls::closeFuncCalled); + EXPECT_EQ(10, NEO::SysCalls::closeFuncArgPassed); + EXPECT_FALSE(session->asyncThreadActive); + + NEO::SysCalls::closeFuncCalled = 0; + NEO::SysCalls::closeFuncArgPassed = 0; +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWithFdEqualZeroWhenClosingConnectionThenSysCallIsNotCalledAndFalseReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 0); + + EXPECT_NE(nullptr, session); + + NEO::SysCalls::closeFuncCalled = 0; + NEO::SysCalls::closeFuncArgPassed = 0; + auto ret = session->closeConnection(); + EXPECT_FALSE(ret); + + EXPECT_EQ(0u, NEO::SysCalls::closeFuncCalled); + EXPECT_EQ(0, NEO::SysCalls::closeFuncArgPassed); + + NEO::SysCalls::closeFuncCalled = 0; + NEO::SysCalls::closeFuncArgPassed = 0; +} + +TEST_F(DebugApiLinuxTest, GivenPrintDebugMessagesWhenDebugSessionClosesConnectionWithErrorThenMessageIsPrinted) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_ERROR); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + ::testing::internal::CaptureStderr(); + + auto session = std::make_unique(config, device, 10); + + EXPECT_NE(nullptr, session); + + NEO::SysCalls::closeFuncCalled = 0; + NEO::SysCalls::closeFuncRetVal = -1; + auto ret = session->closeConnection(); + EXPECT_FALSE(ret); + + EXPECT_EQ(1u, NEO::SysCalls::closeFuncCalled); + EXPECT_EQ(10, NEO::SysCalls::closeFuncArgPassed); + + NEO::SysCalls::closeFuncCalled = 0; + NEO::SysCalls::closeFuncArgPassed = 0; + NEO::SysCalls::closeFuncRetVal = 0; + + auto errorMessage = ::testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string("\nERROR: Debug connection close() on fd: 10 failed: retCode: -1\n"), errorMessage); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWhenCallingIoctlThenIoctlHandlerIsInvokedWithDebugFd) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + EXPECT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + session->ioctl(0, nullptr); + EXPECT_EQ(1, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWhenIoctlFailsThenErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + EXPECT_NE(nullptr, session); + + auto ret = session->ioctl(-1, nullptr); + EXPECT_EQ(-1, ret); + + NEO::SysCalls::setErrno = EBUSY; + ret = session->ioctl(-1, nullptr); + EXPECT_EQ(-1, ret); + + NEO::SysCalls::setErrno = EAGAIN; + ret = session->ioctl(-1, nullptr); + EXPECT_EQ(-1, ret); + + NEO::SysCalls::setErrno = EINTR; + ret = session->ioctl(-1, nullptr); + EXPECT_EQ(-1, ret); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWhenIoctlIsCalledThenItIsForwardedToSysCall) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + EXPECT_NE(nullptr, session); + + auto ret = session->ioctl(10, nullptr); + EXPECT_EQ(0, ret); +} + +TEST_F(DebugApiLinuxTest, 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(DebugApiLinuxTest, WhenCallingReadOrWriteGpuMemoryThenVmOpenIoctlIsCalled) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + for (int i = 0; i < 2; i++) { + + NEO::SysCalls::closeFuncCalled = 0; + + char buffer[bufferSize]; + int retVal = 0; + if (i == 0) { + handler->preadRetVal = bufferSize; + retVal = session->readGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(static_cast(PRELIM_I915_DEBUG_VM_OPEN_READ_ONLY), handler->vmOpen.flags); + } else { + handler->pwriteRetVal = bufferSize; + retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(static_cast(PRELIM_I915_DEBUG_VM_OPEN_READ_WRITE), handler->vmOpen.flags); + } + + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, handler->vmOpen.client_handle); + EXPECT_EQ(static_cast(7), handler->vmOpen.handle); + + EXPECT_EQ(0, retVal); + EXPECT_EQ(handler->vmOpenRetVal, NEO::SysCalls::closeFuncArgPassed); + EXPECT_EQ(1u, NEO::SysCalls::closeFuncCalled); + + handler->vmOpen.flags = 0; + } +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadOrWriteGpuMemoryThenGpuAddressIsDecanonized) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + + char buffer[bufferSize]; + int retVal = 0; + auto gmmHelper = neoDevice->getGmmHelper(); + + handler->preadRetVal = bufferSize; + retVal = session->readGpuMemory(7, buffer, bufferSize, gmmHelper->canonize(0x23000)); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + EXPECT_EQ(0x23000u, handler->preadOffset); + + handler->pwriteRetVal = bufferSize; + retVal = session->writeGpuMemory(7, buffer, bufferSize, gmmHelper->canonize(0x23000)); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + EXPECT_EQ(0x23000u, handler->pwriteOffset); +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadGpuMemoryThenMemoryIsRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + + char output[bufferSize]; + handler->preadRetVal = bufferSize; + auto retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + EXPECT_EQ(0, retVal); + + EXPECT_EQ(1u, handler->mmapCalled == 1 || handler->preadCalled == 1); + + for (int i = 0; i < bufferSize; i++) { + EXPECT_EQ(static_cast(0xaa), output[i]); + } +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerMmapMemoryAccessTrueWhenCallingReadGpuMemoryThenMemoryIsReadWithMmap) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.set(true); + + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + char output[bufferSize]; + auto retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + EXPECT_EQ(0, retVal); + + EXPECT_EQ(1u, handler->mmapCalled); + EXPECT_EQ(1u, handler->munmapCalled); + + for (int i = 0; i < bufferSize; i++) { + EXPECT_EQ(static_cast(0xaa), output[i]); + } +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerMmapMemoryAccessFalseWhenCallingReadGpuMemoryThenMemoryIsReadWithPread) { + + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.set(false); + + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + handler->preadRetVal = bufferSize; //16 bytes to read + char output[bufferSize] = {}; + auto retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + EXPECT_EQ(0, retVal); + + EXPECT_EQ(1u, handler->preadCalled); + EXPECT_EQ(0u, handler->mmapCalled); + + for (int i = 0; i < bufferSize; i++) { + EXPECT_EQ(static_cast(0xaa), output[i]); + } + + handler->preadCalled = 0; + handler->preadRetVal = 8; // read less bytes, iterate + handler->midZeroReturn = 2; + retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + + EXPECT_EQ(4u, handler->preadCalled); + EXPECT_EQ(0u, handler->mmapCalled); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerMmapMemoryAccessFalseWhenPreadFailsThenErrorReturned) { + + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.set(false); + + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + handler->preadRetVal = -1; // fail + char output[bufferSize] = {}; + auto retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + + EXPECT_EQ(1u, handler->preadCalled); + EXPECT_EQ(0u, handler->mmapCalled); + + for (int i = 0; i < bufferSize; i++) { + EXPECT_NE(static_cast(0xaa), output[i]); + } + + handler->preadRetVal = 0; // read 0 and retry + retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + + EXPECT_EQ(4u, handler->preadCalled); + EXPECT_EQ(0u, handler->mmapCalled); +} + +TEST_F(DebugApiLinuxTest, WhenCallingWriteGpuMemoryThenMemoryIsWritten) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + + char buffer[bufferSize]; + memset(buffer, 'O', bufferSize); + handler->pwriteRetVal = bufferSize; + auto retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(0, retVal); + + EXPECT_TRUE(handler->memoryModifiedInMunmap || handler->pwriteCalled == 1); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerMmapMemoryAccessTrueWhenCallingWriteGpuMemoryThenMemoryIsWrittenWithMmap) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.set(true); + + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + char buffer[bufferSize]; + memset(buffer, 'O', bufferSize); + auto retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(0, retVal); + + EXPECT_EQ(1u, handler->mmapCalled); + EXPECT_EQ(1u, handler->munmapCalled); + EXPECT_TRUE(handler->memoryModifiedInMunmap); +} +TEST_F(DebugApiLinuxTest, GivenDebuggerMmapMemoryAccessFalseWhenCallingWriteGpuMemoryThenMemoryIsWrittenWithPwrite) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.set(false); + + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + handler->pwriteRetVal = bufferSize; + + char buffer[bufferSize]; + memset(buffer, 'O', bufferSize); + auto retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(0, retVal); + + EXPECT_EQ(1u, handler->pwriteCalled); + EXPECT_EQ(0u, handler->mmapCalled); + + handler->pwriteCalled = 0; + handler->pwriteRetVal = 8; // not everything written, iterate + handler->midZeroReturn = 2; + retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + + EXPECT_EQ(4u, handler->pwriteCalled); + EXPECT_EQ(0u, handler->mmapCalled); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerMmapMemoryAccessTrueWhenMmapFailesThenReadOrWriteGpuMemoryFails) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.set(true); + + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + handler->mmapFail = true; + char buffer[bufferSize]; + auto retVal = session->readGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + + retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + + EXPECT_EQ(2u, handler->mmapCalled); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerMmapMemoryAccessFalseWhenWhenPwriteFailsThenErrorIsReturned) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.EnableDebuggerMmapMemoryAccess.set(false); + + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + handler->pwriteRetVal = -1; // fail + + char buffer[bufferSize]; + memset(buffer, 'O', bufferSize); + auto retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + + EXPECT_EQ(1u, handler->pwriteCalled); + EXPECT_EQ(0u, handler->mmapCalled); + + handler->pwriteRetVal = 0; // write 0, retry + retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + + EXPECT_EQ(4u, handler->pwriteCalled); + EXPECT_EQ(0u, handler->mmapCalled); +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadMemoryForISAThenMemoryIsRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + ze_device_thread_t thread; + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + zet_debug_memory_space_desc_t desc; + desc.address = 0x1000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + char output[bufferSize]; + // No ISA, ElF or VM yet created. + auto retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + + auto isa = std::make_unique(); + isa->bindInfo = {isaGpuVa, isaSize}; + isa->vmHandle = 3; + isa->elfUuidHandle = DebugSessionLinux::invalidHandle; + isa->moduleBegin = 0; + isa->moduleEnd = 0; + + auto &isaMap = session->clientHandleToConnection[session->clientHandle]->isaMap; + isaMap[isaGpuVa] = std::move(isa); + isaMap[isaGpuVa]->vmBindCounter = 5; + + desc.address = isaGpuVa; + memset(output, 0, bufferSize); + + handler->preadRetVal = bufferSize; + retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + + for (int i = 0; i < bufferSize; i++) { + EXPECT_EQ(static_cast(0xaa), output[i]); + } + + session->allThreadsStopped = true; + + thread.slice = 1; + thread.subslice = 1; + thread.eu = 1; + thread.thread = 1; + + retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadMemoryForELFThenMemoryIsRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + ze_device_thread_t thread; + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + + zet_debug_memory_space_desc_t desc; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + uint64_t elfVa = 0x345000; + uint64_t elfSize = 0xFF; + uint64_t elfUUIDiHandle = 4; + char *elfData = new char[elfSize]; + memset(elfData, 0xa, elfSize); + + session->clientHandleToConnection[session->clientHandle]->elfMap[elfVa] = elfUUIDiHandle; + session->clientHandleToConnection[session->clientHandle]->uuidMap[elfUUIDiHandle].data.reset(elfData); + session->clientHandleToConnection[session->clientHandle]->uuidMap[elfUUIDiHandle].dataSize = elfSize; + + desc.address = elfVa + 0X0F; + char output[bufferSize]; + memset(output, 0, bufferSize); + + handler->pwriteRetVal = bufferSize; + auto retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + + for (int i = 0; i < static_cast(bufferSize); i++) { + EXPECT_EQ(static_cast(0xa), output[i]); + } + + session->allThreadsStopped = true; + + thread.slice = 1; + thread.subslice = 1; + thread.eu = 1; + thread.thread = 1; + + retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + + char *output2 = nullptr; + retVal = session->readMemory(thread, &desc, bufferSize, output2); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadMemoryforAllThreadsOnDefaultMemoryThenMemoryIsRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + ze_device_thread_t thread; + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + zet_debug_memory_space_desc_t desc; + desc.address = 0x1000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + char output[bufferSize]; + + uint64_t vmId = 10; + session->clientHandleToConnection[session->clientHandle]->vmIds.insert(vmId); + + handler->preadRetVal = bufferSize; + auto retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + + for (int i = 0; i < bufferSize; i++) { + EXPECT_EQ(static_cast(0xaa), output[i]); + } + + // Fail with a found VMid. + handler->mmapFail = true; + handler->preadRetVal = -1; + retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadMemoryforASingleThreadThenMemoryIsRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + + session->allThreadsStopped = true; + + ze_device_thread_t thread; + thread.slice = 0; + thread.subslice = 0; + thread.eu = 0; + thread.thread = 0; + zet_debug_memory_space_desc_t desc; + desc.address = 0x1000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + char output[bufferSize]; + session->vmHandle = UINT64_MAX; + session->ensureThreadStopped(thread); + + handler->preadRetVal = bufferSize; + auto retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, retVal); + + session->vmHandle = 7; + session->ensureThreadStopped(thread); + session->clientHandleToConnection[session->clientHandle]->contextsCreated[1].vm = session->vmHandle; + + retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + + for (int i = 0; i < bufferSize; i++) { + EXPECT_EQ(static_cast(0xaa), output[i]); + } +} + +TEST_F(DebugApiLinuxTest, GivenInvalidAddressWhenCallingReadMemoryThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + + session->allThreadsStopped = true; + + ze_device_thread_t thread{0, 0, 0, 0}; + + zet_debug_memory_space_desc_t desc; + desc.address = 0xf0ffffff00000000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + EXPECT_FALSE(session->isValidGpuAddress(desc.address)); + + char output[bufferSize]; + session->vmHandle = UINT64_MAX; + session->ensureThreadStopped(thread); + + handler->preadRetVal = bufferSize; + session->vmHandle = 7; + session->ensureThreadStopped(thread); + session->clientHandleToConnection[session->clientHandle]->contextsCreated[1].vm = session->vmHandle; + + auto retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadMemoryForISAForExpectedFailureCasesThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto &isaMap = session->clientHandleToConnection[session->clientHandle]->isaMap; + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + + auto isa = std::make_unique(); + isa->bindInfo = {isaGpuVa, isaSize}; + isa->vmHandle = 3; + isa->elfUuidHandle = DebugSessionLinux::invalidHandle; + isa->moduleBegin = 0; + isa->moduleEnd = 0; + isaMap[isaGpuVa] = std::move(isa); + isaMap[isaGpuVa]->vmBindCounter = 5; + + NEO::SysCalls::closeFuncCalled = 0; + + ze_device_thread_t thread; + zet_debug_memory_space_desc_t desc; + desc.address = isaGpuVa; + + char output[bufferSize]; + size_t size = bufferSize; + + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_SLM; + + thread.slice = 1; + thread.subslice = 1; + thread.eu = 1; + thread.thread = 1; + + auto retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, retVal); + + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + thread.slice = UINT32_MAX; + thread.subslice = 0; + thread.eu = 0; + thread.thread = 0; + + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = 0; + thread.thread = 0; + + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = 0; + + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + + NEO::SysCalls::setErrno = EINVAL; + handler->mmapFail = true; + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + handler->mmapFail = false; + + desc.address = isaGpuVa + isaSize + 0XF; + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + desc.address = 0x333000; + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + desc.address = isaGpuVa; + size = 0x200F; + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + thread.slice = 0; + thread.subslice = 0; + thread.eu = 0; + thread.thread = 0; + + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, retVal); + + session->clientHandle = MockDebugSessionLinux::invalidClientHandle; + retVal = session->readMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingReadMemoryForElfForExpectedFailureCasesThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + ze_device_thread_t thread; + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + zet_debug_memory_space_desc_t desc; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + uint64_t elfVa = 0x345000; + uint64_t elfSize = 0xFF; + uint64_t elfUUIDiHandle = 4; + char *elfData = new char[elfSize]; + memset(elfData, 0xa, elfSize); + + session->clientHandleToConnection[session->clientHandle]->elfMap[elfVa] = elfUUIDiHandle; + session->clientHandleToConnection[session->clientHandle]->uuidMap[elfUUIDiHandle].data.reset(elfData); + session->clientHandleToConnection[session->clientHandle]->uuidMap[elfUUIDiHandle].dataSize = elfSize; + + char output[bufferSize]; + + desc.address = elfVa + elfSize + 0XF; + auto retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + desc.address = 0x333000; + retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + desc.address = elfVa; + retVal = session->readMemory(thread, &desc, 0x200F, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); +} +TEST_F(DebugApiLinuxTest, WhenCallingWriteMemoryForISAThenMemoryIsWritten) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + ze_device_thread_t thread; + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + zet_debug_memory_space_desc_t desc; + desc.address = 0x1000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + char output[bufferSize]; + // No ISA, ElF or VM yet created. + auto retVal = session->readMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + + auto isa = std::make_unique(); + isa->bindInfo = {isaGpuVa, isaSize}; + isa->vmHandle = 3; + isa->elfUuidHandle = DebugSessionLinux::invalidHandle; + isa->moduleBegin = 0; + isa->moduleEnd = 0; + + auto &isaMap = session->clientHandleToConnection[session->clientHandle]->isaMap; + isaMap[isaGpuVa] = std::move(isa); + isaMap[isaGpuVa]->vmBindCounter = 5; + + desc.address = isaGpuVa; + handler->pwriteRetVal = bufferSize; + + retVal = session->writeMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + EXPECT_EQ(1u, handler->pwriteCalled); + EXPECT_EQ(0u, handler->mmapCalled); + + session->allThreadsStopped = true; + handler->pwriteRetVal = bufferSize; + + thread.slice = 1; + thread.subslice = 1; + thread.eu = 1; + thread.thread = 1; + + retVal = session->writeMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingWriteMemoryForAllThreadsOnDefaultMemoryThenMemoryIsWritten) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + ze_device_thread_t thread; + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + zet_debug_memory_space_desc_t desc; + desc.address = 0x1000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + // No ISA or VM yet created. + char output[bufferSize] = {0, 1, 2, 3}; + auto retVal = session->writeMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + uint64_t vmId = 10; + session->clientHandleToConnection[session->clientHandle]->vmIds.insert(vmId); + + handler->pwriteRetVal = bufferSize; + + retVal = session->writeMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + EXPECT_EQ(1u, handler->pwriteCalled); + EXPECT_EQ(0u, handler->mmapCalled); + + // Fail with a found VMid. + handler->pwriteRetVal = -1; + retVal = session->writeMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingWriteMemoryForASignleThreadThenMemoryIsWritten) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + prelim_drm_i915_debug_event_vm vmEvent = {}; + vmEvent.base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM; + vmEvent.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmEvent.base.size = sizeof(prelim_drm_i915_debug_event_vm); + vmEvent.client_handle = MockDebugSessionLinux::mockClientHandle; + vmEvent.handle = 4; + + session->handleEvent(&vmEvent.base); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.size()); + + session->allThreadsStopped = true; + + ze_device_thread_t thread; + thread.slice = 0; + thread.subslice = 0; + thread.eu = 0; + thread.thread = 0; + zet_debug_memory_space_desc_t desc; + desc.address = 0x1000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + char output[bufferSize] = {0, 1, 2, 3}; + session->vmHandle = UINT64_MAX; + session->ensureThreadStopped(thread); + auto retVal = session->writeMemory(thread, &desc, bufferSize, output); + + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, retVal); + + session->vmHandle = 7; + session->ensureThreadStopped(thread); + session->clientHandleToConnection[session->clientHandle]->contextsCreated[1].vm = session->vmHandle; + + handler->pwriteRetVal = bufferSize; + retVal = session->writeMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_SUCCESS, retVal); + EXPECT_TRUE(handler->memoryModifiedInMunmap || handler->pwriteCalled == 1); +} + +TEST_F(DebugApiLinuxTest, GivenInvalidAddressWhenCallingWriteMemoryThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + + ze_device_thread_t thread{0, 0, 0, 0}; + + zet_debug_memory_space_desc_t desc; + desc.address = 0xf0ffffff00000000; + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + EXPECT_FALSE(session->isValidGpuAddress(desc.address)); + + char output[bufferSize]; + session->vmHandle = UINT64_MAX; + + handler->pwriteRetVal = bufferSize; + session->vmHandle = 7; + session->ensureThreadStopped(thread); + session->clientHandleToConnection[session->clientHandle]->contextsCreated[1].vm = session->vmHandle; + + auto retVal = session->writeMemory(thread, &desc, bufferSize, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); +} + +TEST_F(DebugApiLinuxTest, WhenCallingWriteMemoryForExpectedFailureCasesThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto &isaMap = session->clientHandleToConnection[session->clientHandle]->isaMap; + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + + auto isa = std::make_unique(); + isa->bindInfo = {isaGpuVa, isaSize}; + isa->vmHandle = 3; + isa->elfUuidHandle = DebugSessionLinux::invalidHandle; + isa->moduleBegin = 0; + isa->moduleEnd = 0; + isaMap[isaGpuVa] = std::move(isa); + isaMap[isaGpuVa]->vmBindCounter = 5; + + NEO::SysCalls::closeFuncCalled = 0; + + ze_device_thread_t thread; + zet_debug_memory_space_desc_t desc; + desc.address = isaGpuVa; + + char output[bufferSize]; + size_t size = bufferSize; + + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_SLM; + auto retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, retVal); + + desc.type = ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT; + + thread.slice = UINT32_MAX; + thread.subslice = 0; + thread.eu = 0; + thread.thread = 0; + + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = 0; + thread.thread = 0; + + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = 0; + + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + thread.slice = UINT32_MAX; + thread.subslice = UINT32_MAX; + thread.eu = UINT32_MAX; + thread.thread = UINT32_MAX; + + NEO::SysCalls::setErrno = EINVAL; + errno = EACCES; + handler->mmapFail = true; + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + handler->mmapFail = false; + + desc.address = isaGpuVa + isaSize + 0XF; + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + desc.address = isaGpuVa; + size = 0x200F; + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, retVal); + + desc.address = 0x333000; + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + isaMap.clear(); + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); + + thread.slice = 0; + thread.subslice = 0; + thread.eu = 0; + thread.thread = 0; + + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, retVal); + + session->clientHandle = MockDebugSessionLinux::invalidClientHandle; + retVal = session->writeMemory(thread, &desc, size, output); + EXPECT_EQ(ZE_RESULT_ERROR_UNINITIALIZED, retVal); +} + +TEST_F(DebugApiLinuxTest, GivenErrorFromVmOpenWhenCallingReadGpuMemoryThenCloseIsNotCalledAndErrorReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + handler->vmOpenRetVal = -30; + char output[bufferSize]; + auto retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, handler->vmOpen.client_handle); + EXPECT_EQ(static_cast(7), handler->vmOpen.handle); + EXPECT_EQ(static_cast(PRELIM_I915_DEBUG_VM_OPEN_READ_ONLY), handler->vmOpen.flags); + + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + EXPECT_EQ(0u, NEO::SysCalls::closeFuncCalled); +} + +TEST_F(DebugApiLinuxTest, GivenErrorFromVmOpenWhenCallingWriteGpuMemoryThenCloseIsNotCalledAndErrorReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + handler->vmOpenRetVal = -30; + char buffer[bufferSize]; + auto retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + EXPECT_EQ(0u, NEO::SysCalls::closeFuncCalled); +} + +TEST_F(DebugApiLinuxTest, GivenErrorFromMmapWhenCallingReadGpuMemoryThenCloseIsCalledAndErrnoErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + errno = EACCES; + handler->mmapFail = true; + char output[bufferSize]; + auto retVal = session->readGpuMemory(7, output, bufferSize, 0x23000); + + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, handler->vmOpen.client_handle); + + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + EXPECT_EQ(1u, NEO::SysCalls::closeFuncCalled); +} +TEST_F(DebugApiLinuxTest, GivenErrorFromMmapWhenCallingWriteGpuMemoryThenCloseIsCalledAndErrnoErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + NEO::SysCalls::closeFuncCalled = 0; + errno = EACCES; + handler->mmapFail = true; + char buffer[bufferSize]; + auto retVal = session->writeGpuMemory(7, buffer, bufferSize, 0x23000); + + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, handler->vmOpen.client_handle); + + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, retVal); + EXPECT_EQ(1u, NEO::SysCalls::closeFuncCalled); +} + +TEST_F(DebugApiLinuxTest, givenErrorReturnedFromInitializeWhenDebugSessionIsCreatedThenNullptrIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0; + + ze_result_t result = ZE_RESULT_SUCCESS; + auto session = DebugSession::create(config, device, result); + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWhenPollReturnsZeroThenNotReadyIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + ze_result_t result = ZE_RESULT_SUCCESS; + + mockDrm->context.debuggerOpenRetval = 10; + mockDrm->baseErrno = false; + mockDrm->errnoRetVal = 0; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + result = session->initialize(); + EXPECT_EQ(ZE_RESULT_NOT_READY, result); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWhenClientHandleIsInvalidDuringInitializationThenNotReadyIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + handler->pollRetVal = 1; + handler->debugEventRetVal = -1; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::invalidClientHandle; + + ze_result_t result = session->initialize(); + EXPECT_EQ(ZE_RESULT_NOT_READY, result); + EXPECT_EQ(1, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerLogsWhenReadEventFailsDuringInitializationThenErrorIsPrintedAndReturned) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_ERROR); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + ::testing::internal::CaptureStderr(); + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + handler->pollRetVal = 1; + handler->debugEventRetVal = -1; + session->ioctlHandler.reset(handler); + + ze_result_t result = session->initialize(); + EXPECT_EQ(ZE_RESULT_NOT_READY, result); + EXPECT_EQ(1, handler->ioctlCalled); + + auto errorMessage = ::testing::internal::GetCapturedStderr(); + auto pos = errorMessage.find("PRELIM_I915_DEBUG_IOCTL_READ_EVENT failed: retCode: -1 errno ="); + EXPECT_NE(std::string::npos, pos); +} + +TEST_F(DebugApiLinuxTest, GivenBindInfoForVmHandleWhenReadingModuleDebugAreaThenGpuMemoryIsRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t vmHandle = 6; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToModuleDebugAreaBindInfo[vmHandle] = {0x1234000, 0x1000}; + + DebugAreaHeader debugArea; + debugArea.reserved1 = 1; + debugArea.pgsize = uint8_t(4); + debugArea.version = 1; + + handler->mmapRet = reinterpret_cast(&debugArea); + handler->setPreadMemory(reinterpret_cast(&debugArea), sizeof(session->debugArea), 0x1234000); + handler->preadRetVal = sizeof(session->debugArea); + + auto retVal = session->readModuleDebugArea(); + + EXPECT_TRUE(retVal); + + if (DebugManager.flags.EnableDebuggerMmapMemoryAccess.get()) { + EXPECT_EQ(1, handler->mmapCalled); + EXPECT_EQ(1, handler->munmapCalled); + } else { + EXPECT_EQ(1, handler->preadCalled); + } + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, handler->vmOpen.client_handle); + EXPECT_EQ(vmHandle, handler->vmOpen.handle); + EXPECT_EQ(static_cast(PRELIM_I915_DEBUG_VM_OPEN_READ_ONLY), handler->vmOpen.flags); + + EXPECT_EQ(1u, session->debugArea.reserved1); + EXPECT_EQ(1u, session->debugArea.version); + EXPECT_EQ(4u, session->debugArea.pgsize); +} + +TEST_F(DebugApiLinuxTest, GivenBindInfoForVmHandleWhenReadingModuleDebugAreaReturnsIncorrectDataThenFailIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t vmHandle = 6; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToModuleDebugAreaBindInfo[vmHandle] = {0x1234000, 0x1000}; + + std::vector dummy; + dummy.resize(sizeof(DebugAreaHeader)); + memset(dummy.data(), 2, dummy.size()); + + handler->mmapRet = dummy.data(); + handler->setPreadMemory(dummy.data(), sizeof(session->debugArea), 0x1234000); + handler->preadRetVal = sizeof(session->debugArea); + + auto retVal = session->readModuleDebugArea(); + + EXPECT_FALSE(retVal); + + if (DebugManager.flags.EnableDebuggerMmapMemoryAccess.get()) { + EXPECT_EQ(1, handler->mmapCalled); + EXPECT_EQ(1, handler->munmapCalled); + } else { + EXPECT_EQ(1, handler->preadCalled); + } +} + +TEST_F(DebugApiLinuxTest, GivenBindInfoForVmHandleWhenReadingStateSaveAreaThenGpuMemoryIsRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + auto stateSaveAreaHeader = MockSipData::createStateSaveAreaHeader(2); + handler->mmapRet = stateSaveAreaHeader.data(); + + handler->setPreadMemory(stateSaveAreaHeader.data(), stateSaveAreaHeader.size(), 0x1000); + + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t vmHandle = 6; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle] = {0x1000, stateSaveAreaHeader.size()}; + + session->readStateSaveAreaHeader(); + + if (DebugManager.flags.EnableDebuggerMmapMemoryAccess.get()) { + EXPECT_EQ(1, handler->mmapCalled); + EXPECT_EQ(1, handler->munmapCalled); + } else { + EXPECT_EQ(1, handler->preadCalled); + } + EXPECT_EQ(MockDebugSessionLinux::mockClientHandle, handler->vmOpen.client_handle); + EXPECT_EQ(vmHandle, handler->vmOpen.handle); + EXPECT_EQ(static_cast(PRELIM_I915_DEBUG_VM_OPEN_READ_ONLY), handler->vmOpen.flags); +} + +TEST_F(DebugApiLinuxTest, GivenSizeTooSmallWhenReadingStateSaveAreThenMemoryIsNotRead) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + auto stateSaveAreaHeader = MockSipData::createStateSaveAreaHeader(2); + handler->mmapRet = stateSaveAreaHeader.data(); + + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + handler->vmOpen.handle = 0u; + handler->vmOpen.flags = 0u; + + uint64_t vmHandle = 6; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle] = {0x1000, sizeof(SIP::StateSaveAreaHeader) - 4}; + + session->readStateSaveAreaHeader(); + + EXPECT_EQ(0, handler->mmapCalled); + EXPECT_EQ(0, handler->preadCalled); + EXPECT_EQ(0, handler->munmapCalled); + EXPECT_EQ(0u, handler->vmOpen.handle); + EXPECT_EQ(0u, handler->vmOpen.flags); +} + +TEST_F(DebugApiLinuxTest, GivenErrorInVmOpenWhenReadingModuleDebugAreaThenGpuMemoryIsNotReadAndFalseReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + handler->vmOpenRetVal = -19; + uint64_t vmHandle = 6; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToModuleDebugAreaBindInfo[vmHandle] = {0x1234000, 0x1000}; + + DebugAreaHeader debugArea; + debugArea.reserved1 = 1; + debugArea.pgsize = uint8_t(4); + debugArea.version = 1; + + auto retVal = session->readModuleDebugArea(); + + EXPECT_FALSE(retVal); + EXPECT_EQ(0, handler->mmapCalled); + EXPECT_EQ(0, handler->munmapCalled); + EXPECT_EQ(vmHandle, handler->vmOpen.handle); + EXPECT_STRNE("dbgarea", session->debugArea.magic); +} + +TEST_F(DebugApiLinuxTest, GivenClientContextAndModuleDebugAreaEventsWhenInitializingThenSuccessIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + prelim_drm_i915_debug_event_client client = {}; + + client.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client.base.size = sizeof(prelim_drm_i915_debug_event_client); + client.handle = 1; + + prelim_drm_i915_debug_event_context context = {}; + + context.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT; + context.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + context.base.size = sizeof(prelim_drm_i915_debug_event_context); + context.client_handle = MockDebugSessionLinux::mockClientHandle; + context.handle = 3; + + auto uuidName = NEO::classNamesToUuid[2].first; + auto uuidNameSize = strlen(uuidName); + auto uuidHash = NEO::classNamesToUuid[2].second; + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + uuid.handle = 2; + uuid.class_handle = PRELIM_I915_UUID_CLASS_STRING; + uuid.payload_size = uuidNameSize; + + prelim_drm_i915_debug_read_uuid readUuid = {}; + readUuid.client_handle = MockDebugSessionLinux::mockClientHandle; + memcpy(readUuid.uuid, uuidHash.data(), uuidHash.size()); + readUuid.payload_ptr = reinterpret_cast(uuidName); + readUuid.payload_size = uuid.payload_size; + readUuid.handle = 3; + + prelim_drm_i915_debug_event_uuid uuidDebugArea = {}; + + uuidDebugArea.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuidDebugArea.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuidDebugArea.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuidDebugArea.client_handle = MockDebugSessionLinux::mockClientHandle; + uuidDebugArea.handle = 3; + uuidDebugArea.class_handle = 2; + + uint64_t moduleDebugAddress = 0x34567000; + uint64_t vmBindDebugAreaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindDebugArea = reinterpret_cast(&vmBindDebugAreaData); + + vmBindDebugArea->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindDebugArea->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindDebugArea->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(typeOfUUID); + vmBindDebugArea->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindDebugArea->va_start = moduleDebugAddress; + vmBindDebugArea->va_length = 0x1000; + vmBindDebugArea->vm_handle = 0; + vmBindDebugArea->num_uuids = 1; + auto uuids = reinterpret_cast(ptrOffset(vmBindDebugAreaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidTemp = 3; + memcpy(uuids, &uuidTemp, sizeof(typeOfUUID)); + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + handler->eventQueue.push({reinterpret_cast(&context), static_cast(context.base.size)}); + handler->eventQueue.push({reinterpret_cast(&uuid), static_cast(uuid.base.size)}); + handler->eventQueue.push({reinterpret_cast(&uuidDebugArea), static_cast(uuidDebugArea.base.size)}); + handler->eventQueue.push({reinterpret_cast(vmBindDebugArea), static_cast(vmBindDebugArea->base.size)}); + + handler->returnUuid = &readUuid; + + auto eventsCount = handler->eventQueue.size(); + handler->pollRetVal = 1; + + DebugAreaHeader debugArea; + debugArea.reserved1 = 1; + debugArea.pgsize = uint8_t(4); + debugArea.version = 1; + + handler->mmapRet = reinterpret_cast(&debugArea); + handler->setPreadMemory(reinterpret_cast(&debugArea), sizeof(session->debugArea), moduleDebugAddress); + handler->preadRetVal = sizeof(session->debugArea); + + session->ioctlHandler.reset(handler); + + ze_result_t result = session->initialize(); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + constexpr size_t readUuidCount = 1; + constexpr size_t dummyReadEventCount = 0; + constexpr size_t vmOpenDebugAreaCount = 1; + EXPECT_EQ(eventsCount + readUuidCount + dummyReadEventCount + vmOpenDebugAreaCount, static_cast(handler->ioctlCalled)); +} + +TEST_F(DebugApiLinuxTest, GivenClientContextAndModuleDebugAreaEventsWhenIncorrectModuleDebugAreaReadThenErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + prelim_drm_i915_debug_event_client client = {}; + + client.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client.base.size = sizeof(prelim_drm_i915_debug_event_client); + client.handle = 1; + + prelim_drm_i915_debug_event_context context = {}; + + context.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT; + context.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + context.base.size = sizeof(prelim_drm_i915_debug_event_context); + context.client_handle = MockDebugSessionLinux::mockClientHandle; + context.handle = 3; + + auto uuidName = NEO::classNamesToUuid[2].first; + auto uuidNameSize = strlen(uuidName); + auto uuidHash = NEO::classNamesToUuid[2].second; + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + uuid.handle = 2; + uuid.class_handle = PRELIM_I915_UUID_CLASS_STRING; + uuid.payload_size = uuidNameSize; + + prelim_drm_i915_debug_read_uuid readUuid = {}; + readUuid.client_handle = MockDebugSessionLinux::mockClientHandle; + memcpy(readUuid.uuid, uuidHash.data(), uuidHash.size()); + readUuid.payload_ptr = reinterpret_cast(uuidName); + readUuid.payload_size = uuid.payload_size; + readUuid.handle = 3; + + prelim_drm_i915_debug_event_uuid uuidDebugArea = {}; + + uuidDebugArea.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuidDebugArea.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuidDebugArea.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuidDebugArea.client_handle = MockDebugSessionLinux::mockClientHandle; + uuidDebugArea.handle = 3; + uuidDebugArea.class_handle = 2; + + uint64_t moduleDebugAddress = 0x34567000; + uint64_t vmBindDebugAreaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindDebugArea = reinterpret_cast(&vmBindDebugAreaData); + + vmBindDebugArea->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindDebugArea->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindDebugArea->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(typeOfUUID); + vmBindDebugArea->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindDebugArea->va_start = moduleDebugAddress; + vmBindDebugArea->va_length = 0x1000; + vmBindDebugArea->vm_handle = 0; + vmBindDebugArea->num_uuids = 1; + auto uuids = reinterpret_cast(ptrOffset(vmBindDebugAreaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidTemp = 3; + memcpy(uuids, &uuidTemp, sizeof(typeOfUUID)); + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + handler->eventQueue.push({reinterpret_cast(&context), static_cast(context.base.size)}); + handler->eventQueue.push({reinterpret_cast(&uuid), static_cast(uuid.base.size)}); + handler->eventQueue.push({reinterpret_cast(&uuidDebugArea), static_cast(uuidDebugArea.base.size)}); + handler->eventQueue.push({reinterpret_cast(vmBindDebugArea), static_cast(vmBindDebugArea->base.size)}); + + handler->returnUuid = &readUuid; + handler->pollRetVal = 1; + + session->ioctlHandler.reset(handler); + + ze_result_t result = session->initialize(); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); + EXPECT_STRNE("dbgarea", session->debugArea.magic); +} + +TEST_F(DebugApiLinuxTest, GivenNoClientHandleSetWhenCheckingEventsCollectedThenFalseIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = MockDebugSessionLinux::invalidClientHandle; + + EXPECT_FALSE(session->checkAllEventsCollected()); +} + +TEST_F(DebugApiLinuxTest, GivenClientHandleSetButNoModuleDebugAreaWhenCheckingEventsCollectedThenFalseIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + EXPECT_FALSE(session->checkAllEventsCollected()); +} + +TEST_F(DebugApiLinuxTest, GivenTwoClientEventsWithTheSameHandleWhenInitializingThenDebugBreakIsCalledAndConnectionIsOverriden) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + const uint64_t clientHandle = 2; + + prelim_drm_i915_debug_event_client client = {}; + client.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client.base.size = sizeof(prelim_drm_i915_debug_event_client); + client.handle = clientHandle; + + prelim_drm_i915_debug_event_client client2 = {}; + client2.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client2.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client2.base.size = sizeof(prelim_drm_i915_debug_event_client); + client2.handle = clientHandle; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + handler->eventQueue.push({reinterpret_cast(&client2), static_cast(client2.base.size)}); + handler->pollRetVal = 1; + + session->ioctlHandler.reset(handler); + session->initialize(); + + EXPECT_NE(nullptr, session->clientHandleToConnection[clientHandle]); + EXPECT_EQ(3, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionWhenClientCreateAndDestroyEventsReadOnInitializationThenDeviceLostErrorReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + prelim_drm_i915_debug_event_client clientCreate = {}; + clientCreate.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + clientCreate.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + clientCreate.base.size = sizeof(prelim_drm_i915_debug_event_client); + clientCreate.handle = 1; + + prelim_drm_i915_debug_event_client clientDestroy = {}; + clientDestroy.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + clientDestroy.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + clientDestroy.base.size = sizeof(prelim_drm_i915_debug_event_client); + clientDestroy.handle = 1; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&clientCreate), static_cast(clientCreate.base.size)}); + handler->eventQueue.push({reinterpret_cast(&clientDestroy), static_cast(clientDestroy.base.size)}); + handler->pollRetVal = 1; + auto eventsCount = handler->eventQueue.size(); + + session->ioctlHandler.reset(handler); + session->clientHandle = 1; + + ze_result_t result = session->initialize(); + EXPECT_EQ(ZE_RESULT_ERROR_DEVICE_LOST, result); + EXPECT_EQ(eventsCount + 1, static_cast(handler->ioctlCalled)); +} + +TEST_F(DebugApiLinuxTest, GivenDebugSessionInitializationWhenReadEventHasInvalidFlagsThenUnknownErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + prelim_drm_i915_debug_event_client clientInvalidFlag = {}; + clientInvalidFlag.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + clientInvalidFlag.base.flags = 0x8000; + clientInvalidFlag.base.size = sizeof(prelim_drm_i915_debug_event_client); + clientInvalidFlag.handle = 1; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&clientInvalidFlag), static_cast(clientInvalidFlag.base.size)}); + handler->pollRetVal = 1; + auto eventsCount = handler->eventQueue.size(); + + session->ioctlHandler.reset(handler); + + ze_result_t result = session->initialize(); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); + EXPECT_EQ(eventsCount, static_cast(handler->ioctlCalled)); +} + +TEST_F(DebugApiLinuxTest, GivenInvalidFlagsWhenReadingEventThenUnknownErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + prelim_drm_i915_debug_event_client clientInvalidFlag = {}; + clientInvalidFlag.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + clientInvalidFlag.base.flags = 0x8000; + clientInvalidFlag.base.size = sizeof(prelim_drm_i915_debug_event_client); + clientInvalidFlag.handle = 1; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&clientInvalidFlag), static_cast(clientInvalidFlag.base.size)}); + handler->pollRetVal = 1; + + session->ioctlHandler.reset(handler); + + uint64_t data[512]; + auto drmDebugEvent = reinterpret_cast(data); + + ze_result_t result = session->readEventImp(drmDebugEvent); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); + EXPECT_EQ(1u, static_cast(handler->ioctlCalled)); +} + +TEST_F(DebugApiLinuxTest, GivenValidFlagsWhenReadingEventThenEventIsProcessed) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + uint64_t ctxHandle = 2; + uint64_t vmHandle = 7; + uint64_t lrcHandle = 8; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->lrcToContextHandle[lrcHandle] = ctxHandle; + + prelim_drm_i915_debug_event_eu_attention attEvent = {}; + attEvent.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attEvent.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE; + attEvent.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention); + attEvent.bitmask_size = 0; + attEvent.client_handle = MockDebugSessionLinux::mockClientHandle; + attEvent.lrc_handle = lrcHandle; + attEvent.flags = 0; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&attEvent), static_cast(attEvent.base.size)}); + handler->pollRetVal = 1; + + session->ioctlHandler.reset(handler); + + uint64_t data[512]; + auto drmDebugEvent = reinterpret_cast(data); + ze_result_t result = session->readEventImp(drmDebugEvent); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(1u, session->handleEventCalledCount); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerLogsAndUnhandledEventTypeWhenHandlingEventThenMessageIsPrinted) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_INFO); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + prelim_drm_i915_debug_event event = {}; + + event.type = PRELIM_DRM_I915_DEBUG_EVENT_MAX_EVENT + 1; + event.flags = 0; + event.size = sizeof(prelim_drm_i915_debug_event); + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&event), static_cast(event.size)}); + handler->pollRetVal = 1; + + session->ioctlHandler.reset(handler); + + ::testing::internal::CaptureStdout(); + + session->handleEvent(&event); + + auto errorMessage = ::testing::internal::GetCapturedStdout(); + std::stringstream expectedMessage; + expectedMessage << "PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: UNHANDLED "; + expectedMessage << PRELIM_DRM_I915_DEBUG_EVENT_MAX_EVENT + 1; + expectedMessage << " flags = 0 size ="; + auto pos = errorMessage.find(expectedMessage.str()); + EXPECT_NE(std::string::npos, pos); +} + +TEST_F(DebugApiLinuxTest, GivenContextCreateAndDestroyEventsWhenInitializingThenOnlyValidContextsAreStored) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + uint64_t clientHandle = 2; + prelim_drm_i915_debug_event_client client = {}; + client.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client.base.size = sizeof(prelim_drm_i915_debug_event_client); + client.handle = clientHandle; + + prelim_drm_i915_debug_event_context context = {}; + context.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT; + context.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + context.base.size = sizeof(prelim_drm_i915_debug_event_context); + context.client_handle = clientHandle; + context.handle = 3; + + prelim_drm_i915_debug_event_context context2 = {}; + context2.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT, + context2.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE, + context2.base.size = sizeof(prelim_drm_i915_debug_event_context); + context2.client_handle = clientHandle; + context2.handle = 4; + + prelim_drm_i915_debug_event_context contextDestroy = {}; + contextDestroy.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT; + contextDestroy.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + contextDestroy.base.size = sizeof(prelim_drm_i915_debug_event_context); + contextDestroy.client_handle = clientHandle; + contextDestroy.handle = 3; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + handler->eventQueue.push({reinterpret_cast(&context), static_cast(context.base.size)}); + handler->eventQueue.push({reinterpret_cast(&context2), static_cast(context2.base.size)}); + handler->eventQueue.push({reinterpret_cast(&contextDestroy), static_cast(contextDestroy.base.size)}); + + auto eventsCount = handler->eventQueue.size(); + handler->pollRetVal = 1; + + session->ioctlHandler.reset(handler); + session->initialize(); + + EXPECT_EQ(eventsCount + 1, static_cast(handler->ioctlCalled)); + + EXPECT_EQ(1u, session->clientHandleToConnection[clientHandle]->contextsCreated.size()); + EXPECT_EQ(session->clientHandleToConnection[clientHandle]->contextsCreated.end(), session->clientHandleToConnection[clientHandle]->contextsCreated.find(context.handle)); +} + +TEST_F(DebugApiLinuxTest, GivenUuidEventForClassWhenHandlingEventThenClassHandleIsSavedWithNameAndIndex) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto uuidName = NEO::classNamesToUuid[0].first; + auto uuidNameSize = strlen(uuidName); + auto uuidHash = NEO::classNamesToUuid[0].second; + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = 1; + uuid.handle = 2; + uuid.payload_size = uuidNameSize; + + prelim_drm_i915_debug_read_uuid readUuid = {}; + readUuid.client_handle = 1; + memcpy(readUuid.uuid, uuidHash.data(), uuidHash.size()); + readUuid.payload_ptr = reinterpret_cast(uuidName); + readUuid.payload_size = uuid.payload_size; + readUuid.handle = 2; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + handler->returnUuid = &readUuid; + + session->handleEvent(reinterpret_cast(&uuid)); + + auto iter = session->getClassHandleToIndex().find(readUuid.handle); + EXPECT_NE(session->getClassHandleToIndex().end(), iter); + + auto classNameIndex = iter->second; + EXPECT_EQ(uuidName, classNameIndex.first); + EXPECT_EQ(0u, classNameIndex.second); +} + +TEST_F(DebugApiLinuxTest, GivenUuidEventWithPayloadWhenHandlingEventThenReadEventIoctlIsCalledAndUuidDataIsStored) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + const char uuidString[] = "31203221-8069-5a0a-0000-55742d6ea000"; + std::string elf("ELF"); + prelim_drm_i915_debug_event_uuid uuidElf = {}; + uuidElf.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuidElf.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuidElf.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuidElf.client_handle = MockDebugSessionLinux::mockClientHandle; + uuidElf.handle = 3; + uuidElf.class_handle = 2; + + uuidElf.payload_size = elf.size(); + + prelim_drm_i915_debug_read_uuid readUuidElf = {}; + memcpy(readUuidElf.uuid, uuidString, 36); + readUuidElf.payload_ptr = reinterpret_cast(elf.c_str()); + readUuidElf.payload_size = elf.size(); + readUuidElf.handle = 3; + + handler->returnUuid = &readUuidElf; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[2] = {"ELF", static_cast(NEO::DrmResourceClass::Elf)}; + + session->handleEvent(&uuidElf.base); + + EXPECT_EQ(uuidElf.handle, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].handle); + EXPECT_EQ(uuidElf.class_handle, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].classHandle); + EXPECT_NE(nullptr, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].data); + EXPECT_EQ(elf.size(), session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].dataSize); + EXPECT_EQ(0x55742d6ea000u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].ptr); + + std::string registeredElf(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].data.get(), session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].dataSize); + EXPECT_EQ(elf, registeredElf); + + EXPECT_EQ(1, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenUuidEventWhenHandlingThenUuidIsInsertedToMap) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + uuid.handle = 2; + uuid.payload_size = 0; + + auto handler = new MockIoctlHandler; + handler->eventQueue.push({reinterpret_cast(&uuid), static_cast(uuid.base.size)}); + handler->pollRetVal = 1; + + session->ioctlHandler.reset(handler); + session->handleEvent(&uuid.base); + + EXPECT_EQ(1u, session->clientHandleToConnection[uuid.client_handle]->uuidMap.size()); + EXPECT_NE(session->clientHandleToConnection[uuid.client_handle]->uuidMap.end(), session->clientHandleToConnection[uuid.client_handle]->uuidMap.find(uuid.handle)); + + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + session->handleEvent(&uuid.base); + EXPECT_NE(session->clientHandleToConnection[uuid.client_handle]->uuidMap.end(), session->clientHandleToConnection[uuid.client_handle]->uuidMap.find(uuid.handle)); +} + +TEST_F(DebugApiLinuxTest, GivenUuidEventForL0ZebinModuleWhenHandlingEventThenKernelCountFromPayloadIsRead) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + const char uuidString[] = "31203221-8069-5a0a-0000-55742d6ea000"; + uint32_t kernelCount = 5; + prelim_drm_i915_debug_event_uuid l0ModuleUuid = {}; + l0ModuleUuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + l0ModuleUuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + l0ModuleUuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + l0ModuleUuid.client_handle = MockDebugSessionLinux::mockClientHandle; + l0ModuleUuid.handle = 3; + l0ModuleUuid.class_handle = 2; + + l0ModuleUuid.payload_size = sizeof(kernelCount); + + prelim_drm_i915_debug_read_uuid readUuidElf = {}; + memcpy(readUuidElf.uuid, uuidString, 36); + readUuidElf.payload_ptr = reinterpret_cast(&kernelCount); + readUuidElf.payload_size = sizeof(kernelCount); + readUuidElf.handle = 3; + + handler->returnUuid = &readUuidElf; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[2] = {"L0_MODULE", static_cast(NEO::DrmResourceClass::L0ZebinModule)}; + + session->handleEvent(&l0ModuleUuid.base); + + EXPECT_NE(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.end(), + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.find(l0ModuleUuid.handle)); + + EXPECT_EQ(kernelCount, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[l0ModuleUuid.handle].segmentCount); + + EXPECT_EQ(l0ModuleUuid.handle, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[l0ModuleUuid.handle].handle); + EXPECT_EQ(l0ModuleUuid.class_handle, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[l0ModuleUuid.handle].classHandle); + EXPECT_NE(nullptr, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[l0ModuleUuid.handle].data); + EXPECT_EQ(sizeof(kernelCount), session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[l0ModuleUuid.handle].dataSize); + + EXPECT_EQ(1, handler->ioctlCalled); + + // inject module segment + auto &module = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[l0ModuleUuid.handle]; + module.loadAddresses.insert(0x12340000); + + l0ModuleUuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + l0ModuleUuid.payload_size = 0; + session->handleEvent(&l0ModuleUuid.base); + + EXPECT_EQ(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.end(), + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.find(l0ModuleUuid.handle)); +} + +TEST_F(DebugApiLinuxTest, GivenUuidEventWithNonElfClassHandleWhenHandlingEventThenUuidDataPtrIsNotSet) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + const char uuidString[] = "31203221-8069-5a0a-0000-55742d6ea000"; + std::string elf("ELF"); + prelim_drm_i915_debug_event_uuid uuidElf = {}; + uuidElf.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuidElf.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuidElf.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuidElf.client_handle = MockDebugSessionLinux::mockClientHandle; + uuidElf.handle = 3; + uuidElf.class_handle = 2; + + uuidElf.payload_size = elf.size(); + + prelim_drm_i915_debug_read_uuid readUuidElf = {}; + memcpy(readUuidElf.uuid, uuidString, 36); + readUuidElf.payload_ptr = reinterpret_cast(elf.c_str()); + readUuidElf.payload_size = elf.size(); + readUuidElf.handle = 3; + handler->returnUuid = &readUuidElf; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex.erase(2); + + session->handleEvent(&uuidElf.base); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuidElf.handle].ptr); + EXPECT_EQ(1, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenVmBindEventWithUnknownUUIDWhenHandlingEventThenEventIsIgnored) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + uint64_t sbaClassHandle = 1; + uint64_t sbaHandle = 4; + uint64_t sbaAddress = 0x1234000; + + DebugSessionLinux::UuidData sbaUuidData = { + .handle = sbaHandle, + .classHandle = sbaClassHandle, + .classIndex = NEO::DrmResourceClass::SbaTrackingBuffer}; + + uint64_t vmBindSbaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindSba = reinterpret_cast(&vmBindSbaData); + + vmBindSba->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindSba->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindSba->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(typeOfUUID); + vmBindSba->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindSba->va_start = sbaAddress; + vmBindSba->va_length = 0x1000; + vmBindSba->vm_handle = 0; + vmBindSba->num_uuids = 1; + auto uuids = reinterpret_cast(ptrOffset(vmBindSbaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + auto uuidTemp = static_cast(sbaHandle); + memcpy(uuids, &uuidTemp, sizeof(typeOfUUID)); + + session->handleEvent(&vmBindSba->base); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo.size()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToModuleDebugAreaBindInfo.size()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo.size()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.size()); +} + +TEST_F(DebugApiLinuxTest, GivenVmBindEventWithUuidOfUnknownClassWhenHandlingEventThenNoBindInfoIsStored) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + uint64_t sbaClassHandle = 1; + uint64_t sbaHandle = 4; + uint64_t sbaAddress = 0x1234000; + + DebugSessionLinux::UuidData sbaUuidData = { + .handle = sbaHandle, + .classHandle = sbaClassHandle, + .classIndex = NEO::DrmResourceClass::SbaTrackingBuffer}; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(sbaHandle, std::move(sbaUuidData)); + + uint64_t vmBindSbaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindSba = reinterpret_cast(&vmBindSbaData); + + vmBindSba->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindSba->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindSba->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(typeOfUUID); + vmBindSba->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindSba->va_start = sbaAddress; + vmBindSba->va_length = 0x1000; + vmBindSba->vm_handle = 0; + vmBindSba->num_uuids = 1; + auto uuids = reinterpret_cast(ptrOffset(vmBindSbaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidTemp = static_cast(sbaHandle); + memcpy(uuids, &uuidTemp, sizeof(typeOfUUID)); + + session->handleEvent(&vmBindSba->base); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo.size()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToModuleDebugAreaBindInfo.size()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.size()); +} + +TEST_F(DebugApiLinuxTest, GivenUuidEventOfKnownClassWhenHandlingEventThenGpuAddressIsSavedFromPayload) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + uint64_t sbaClassHandle = 1; + uint64_t moduleDebugClassHandle = 2; + uint64_t contextSaveClassHandle = 3; + + uint64_t sbaAddress = 0x1234000; + uint64_t moduleDebugAddress = 0x34567000; + uint64_t contextSaveAddress = 0x56789000; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[sbaClassHandle] = {"SBA AREA", static_cast(NEO::DrmResourceClass::SbaTrackingBuffer)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[moduleDebugClassHandle] = {"DEBUG AREA", static_cast(NEO::DrmResourceClass::ModuleHeapDebugArea)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[contextSaveClassHandle] = {"CONTEXT SAVE AREA", static_cast(NEO::DrmResourceClass::ContextSaveArea)}; + + prelim_drm_i915_debug_event_uuid uuidSba = {}; + uuidSba.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuidSba.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuidSba.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuidSba.client_handle = MockDebugSessionLinux::mockClientHandle; + uuidSba.handle = 4; + uuidSba.class_handle = sbaClassHandle; + uuidSba.payload_size = sizeof(sbaAddress); + + prelim_drm_i915_debug_read_uuid readUuid = {}; + readUuid.payload_ptr = reinterpret_cast(&sbaAddress); + readUuid.payload_size = sizeof(sbaAddress); + readUuid.handle = 4; + + handler->returnUuid = &readUuid; + session->handleEvent(&uuidSba.base); + + prelim_drm_i915_debug_event_uuid uuidModule = {}; + uuidModule.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuidModule.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuidModule.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuidModule.client_handle = MockDebugSessionLinux::mockClientHandle; + uuidModule.handle = 5; + uuidModule.class_handle = moduleDebugClassHandle; + uuidModule.payload_size = sizeof(moduleDebugAddress); + + readUuid = {}; + readUuid.payload_ptr = reinterpret_cast(&moduleDebugAddress); + readUuid.payload_size = sizeof(moduleDebugAddress); + readUuid.handle = 5; + + handler->returnUuid = &readUuid; + session->handleEvent(&uuidModule.base); + + prelim_drm_i915_debug_event_uuid uuidContextSave = {}; + + uuidContextSave.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuidContextSave.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuidContextSave.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuidContextSave.client_handle = MockDebugSessionLinux::mockClientHandle, + uuidContextSave.handle = 6, + uuidContextSave.class_handle = contextSaveClassHandle; + uuidContextSave.payload_size = sizeof(contextSaveAddress); + + readUuid = {}; + readUuid.payload_ptr = reinterpret_cast(&contextSaveAddress); + readUuid.payload_size = sizeof(contextSaveAddress); + readUuid.handle = 6; + + handler->returnUuid = &readUuid; + session->handleEvent(&uuidContextSave.base); + + EXPECT_EQ(moduleDebugAddress, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->moduleDebugAreaGpuVa); + EXPECT_EQ(sbaAddress, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->stateBaseAreaGpuVa); + EXPECT_EQ(contextSaveAddress, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextStateSaveAreaGpuVa); +} + +struct DebugApiLinuxVmBindFixture : public DebugApiLinuxFixture { + void SetUp() { + DebugApiLinuxFixture::SetUp(); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[sbaClassHandle] = {"SBA AREA", static_cast(NEO::DrmResourceClass::SbaTrackingBuffer)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[moduleDebugClassHandle] = {"DEBUG AREA", static_cast(NEO::DrmResourceClass::ModuleHeapDebugArea)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[contextSaveClassHandle] = {"CONTEXT SAVE AREA", static_cast(NEO::DrmResourceClass::ContextSaveArea)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[isaClassHandle] = {"ISA", static_cast(NEO::DrmResourceClass::Isa)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[elfClassHandle] = {"ELF", static_cast(NEO::DrmResourceClass::Elf)}; + + DebugSessionLinux::UuidData isaUuidData = { + .handle = isaUUID, + .classHandle = isaClassHandle, + .classIndex = NEO::DrmResourceClass::Isa}; + DebugSessionLinux::UuidData elfUuidData = { + .handle = elfUUID, + .classHandle = elfClassHandle, + .classIndex = NEO::DrmResourceClass::Elf, + .data = std::make_unique(10), + .dataSize = 10}; + + DebugSessionLinux::UuidData cookieUuidData = { + .handle = cookieUUID, + .classHandle = isaUUID, + .classIndex = NEO::DrmResourceClass::MaxSize, + }; + DebugSessionLinux::UuidData sbaUuidData = { + .handle = sbaUUID, + .classHandle = sbaClassHandle, + .classIndex = NEO::DrmResourceClass::SbaTrackingBuffer, + .data = nullptr, + .dataSize = 0}; + + DebugSessionLinux::UuidData debugAreaUuidData = { + .handle = debugAreaUUID, + .classHandle = moduleDebugClassHandle, + .classIndex = NEO::DrmResourceClass::ModuleHeapDebugArea, + .data = nullptr, + .dataSize = 0}; + + DebugSessionLinux::UuidData stateSaveUuidData = { + .handle = stateSaveUUID, + .classHandle = contextSaveClassHandle, + .classIndex = NEO::DrmResourceClass::ContextSaveArea, + .data = nullptr, + .dataSize = 0}; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(isaUUID, std::move(isaUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(elfUUID, std::move(elfUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(cookieUUID, std::move(cookieUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(sbaUUID, std::move(sbaUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(debugAreaUUID, std::move(debugAreaUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(stateSaveUUID, std::move(stateSaveUuidData)); + } + + void TearDown() { + DebugApiLinuxFixture::TearDown(); + } + + const uint64_t sbaClassHandle = 1; + const uint64_t moduleDebugClassHandle = 2; + const uint64_t contextSaveClassHandle = 3; + const uint64_t isaClassHandle = 4; + const uint64_t elfClassHandle = 5; + const uint64_t zebinModuleClassHandle = 101; + const uint64_t vmHandleForVmBind = 3; + + const uint64_t isaUUID = 2; + const uint64_t elfUUID = 3; + const uint64_t cookieUUID = 5; + const uint64_t sbaUUID = 6; + const uint64_t debugAreaUUID = 7; + const uint64_t stateSaveUUID = 8; + const uint64_t zebinModuleUUID = 9; + + std::unique_ptr session; +}; + +using DebugApiLinuxVmBindTest = Test; + +TEST_F(DebugApiLinuxVmBindTest, GivenVmBindEventWithKnownUuidClassWhenHandlingEventThenBindInfoIsStoredForVm) { + uint64_t sbaAddress = 0x1234000; + uint64_t moduleDebugAddress = 0x34567000; + uint64_t contextSaveAddress = 0x56789000; + + uint64_t vmBindSbaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + sizeof(typeOfUUID)]; + uint64_t vmBindDebugAreaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + sizeof(typeOfUUID)]; + uint64_t vmBindContextSaveAreaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + sizeof(typeOfUUID)]; + + prelim_drm_i915_debug_event_vm_bind *vmBindSba = reinterpret_cast(&vmBindSbaData); + prelim_drm_i915_debug_event_vm_bind *vmBindDebugArea = reinterpret_cast(&vmBindDebugAreaData); + prelim_drm_i915_debug_event_vm_bind *vmBindContextArea = reinterpret_cast(&vmBindContextSaveAreaData); + + vmBindSba->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindSba->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindSba->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(decltype(prelim_drm_i915_debug_event_vm_bind::uuids[0])); + vmBindSba->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindSba->va_start = sbaAddress; + vmBindSba->va_length = 0x1000; + vmBindSba->vm_handle = vmHandleForVmBind; + vmBindSba->num_uuids = 1; + auto *uuids = reinterpret_cast(ptrOffset(vmBindSbaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + auto temp = static_cast(sbaUUID); + memcpy(uuids, &temp, sizeof(typeOfUUID)); + + vmBindDebugArea->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindDebugArea->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindDebugArea->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(decltype(prelim_drm_i915_debug_event_vm_bind::uuids[0])); + vmBindDebugArea->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindDebugArea->va_start = moduleDebugAddress; + vmBindDebugArea->va_length = 0x1000; + vmBindDebugArea->vm_handle = 4; + vmBindDebugArea->num_uuids = 1; + uuids = reinterpret_cast(ptrOffset(vmBindDebugAreaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + temp = static_cast(debugAreaUUID); + memcpy(uuids, &temp, sizeof(typeOfUUID)); + + vmBindContextArea->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindContextArea->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindContextArea->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(decltype(prelim_drm_i915_debug_event_vm_bind::uuids[0])); + vmBindContextArea->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindContextArea->va_start = contextSaveAddress; + vmBindContextArea->va_length = 0x1000; + vmBindContextArea->vm_handle = 5; + vmBindContextArea->num_uuids = 1; + uuids = reinterpret_cast(ptrOffset(vmBindContextSaveAreaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + temp = static_cast(stateSaveUUID); + memcpy(uuids, &temp, sizeof(typeOfUUID)); + + session->handleEvent(&vmBindSba->base); + session->handleEvent(&vmBindDebugArea->base); + session->handleEvent(&vmBindContextArea->base); + + EXPECT_EQ(moduleDebugAddress, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToModuleDebugAreaBindInfo[4].gpuVa); + EXPECT_EQ(0x1000u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToModuleDebugAreaBindInfo[4].size); + + EXPECT_EQ(sbaAddress, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo[3].gpuVa); + EXPECT_EQ(0x1000u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo[3].size); + + EXPECT_EQ(contextSaveAddress, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[5].gpuVa); + EXPECT_EQ(0x1000u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[5].size); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenZeruNumUuidsWhenHandlingVmBindEventThenNoBindInfoIsStored) { + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + uint64_t sbaAddress = 0x1234000; + prelim_drm_i915_debug_event_vm_bind vmBindSba = {}; + + vmBindSba.base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindSba.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindSba.base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(typeOfUUID); + vmBindSba.client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindSba.va_start = sbaAddress; + vmBindSba.va_length = 0x1000; + vmBindSba.num_uuids = 0; + + session->handleVmBindEvent(&vmBindSba); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo.size()); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenNeedsAckFlagWhenHandlingVmBindEventThenAckIsSent) { + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + uint64_t address = 0x1234000; + prelim_drm_i915_debug_event_vm_bind vmBind = {}; + + vmBind.base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBind.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE | PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK; + vmBind.base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + sizeof(typeOfUUID); + vmBind.base.seqno = 45; + vmBind.client_handle = MockDebugSessionLinux::mockClientHandle; + vmBind.va_start = address; + vmBind.va_length = 0x1000; + vmBind.num_uuids = 0; + + session->handleVmBindEvent(&vmBind); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(vmBind.base.seqno, handler->debugEventAcked.seqno); + EXPECT_EQ(vmBind.base.type, handler->debugEventAcked.type); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenEventWithAckFlagWhenHandlingEventForISAThenEventToAckIsPushed) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE | PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->base.seqno = 20u; + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->eventsToAck.size()); + + auto event = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.front(); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.pop(); + + EXPECT_EQ(ZET_DEBUG_EVENT_FLAG_NEED_ACK, event.flags); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_LOAD, event.type); + + auto isaIter = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap.find(isaGpuVa); + ASSERT_NE(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap.end(), isaIter); + + EXPECT_EQ(1u, isaIter->second->ackEvents.size()); + auto ackedEvent = isaIter->second->ackEvents[0]; + + EXPECT_EQ(vmBindIsa->base.flags, ackedEvent.flags); + EXPECT_EQ(vmBindIsa->base.seqno, ackedEvent.seqno); + EXPECT_EQ(vmBindIsa->base.type, ackedEvent.type); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenEventForISAWhenModuleLoadEventAlreadyAckedThenEventIsAckedImmediatelyAndNotPushed) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE | PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->base.seqno = 20u; + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + auto isaIter = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap.find(isaGpuVa); + ASSERT_NE(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap.end(), isaIter); + EXPECT_EQ(1u, isaIter->second->ackEvents.size()); + + auto event = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.front(); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.pop(); + + session->acknowledgeEvent(&event); + EXPECT_EQ(0u, isaIter->second->ackEvents.size()); + + vmBindIsa->vm_handle = vmHandleForVmBind + 100; + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(0u, isaIter->second->ackEvents.size()); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenIsaRemovedWhenModuleLoadEventIsAckedThenSuccessReturned) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE | PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->base.seqno = 20u; + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + auto isaIter = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap.find(isaGpuVa); + ASSERT_NE(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap.end(), isaIter); + EXPECT_EQ(1u, isaIter->second->ackEvents.size()); + + zet_debug_event_t event = {}; + auto result = session->readEvent(0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap.clear(); + + result = session->acknowledgeEvent(&event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenVmBindEventWithInvalidNumUUIDsWhenHandlingEventThenBindInfoIsNotStored) { + prelim_drm_i915_debug_event_vm_bind vmBindSba = {}; + uint64_t sbaAddress = 0x1234000; + + vmBindSba.base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindSba.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindSba.base.size = sizeof(prelim_drm_i915_debug_event_vm_bind); + vmBindSba.client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindSba.va_start = sbaAddress; + vmBindSba.va_length = 0x1000; + vmBindSba.num_uuids = 10; + + session->handleEvent(&vmBindSba.base); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo.size()); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenVmBindEventForIsaWhenHandlingEventThenIsaAllocationIsSaved) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + auto &isaMap = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap; + + EXPECT_EQ(1u, isaMap.size()); + EXPECT_NE(isaMap.end(), isaMap.find(isaGpuVa)); + + auto isaAllocation = isaMap[isaGpuVa].get(); + EXPECT_EQ(isaGpuVa, isaAllocation->bindInfo.gpuVa); + EXPECT_EQ(isaSize, isaAllocation->bindInfo.size); + EXPECT_EQ(elfUUID, isaAllocation->elfUuidHandle); + EXPECT_EQ(3u, isaAllocation->vmHandle); + EXPECT_EQ(1u, isaAllocation->cookies.size()); + EXPECT_EQ(cookieUUID, *isaAllocation->cookies.begin()); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenTwoVmBindEventForTheSameIsaInDifferentVMWhenHandlingEventThenIsaVmHandleIsNotOverriden) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + auto &isaMap = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap; + EXPECT_EQ(1u, isaMap.size()); + EXPECT_NE(isaMap.end(), isaMap.find(isaGpuVa)); + auto isaAllocation = isaMap[isaGpuVa].get(); + EXPECT_EQ(3u, isaAllocation->vmHandle); + + // Override VM HANDLE + vmBindIsa->vm_handle = 100; + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(1u, isaMap.size()); + isaAllocation = isaMap[isaGpuVa].get(); + EXPECT_EQ(3u, isaAllocation->vmHandle); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenVmBindDestroyEventForIsaWhenHandlingEventThenIsaEntryIsNotSaved) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + auto &isaMap = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap; + EXPECT_EQ(0u, isaMap.size()); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenVmBindEventForIsaWhenReadingEventThenModuleLoadEventIsReturned) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + // No event to ACK if vmBind doesn't have ACK flag + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->eventsToAck.size()); + + zet_debug_event_t event = {}; + ze_result_t result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_LOAD, event.type); + EXPECT_EQ(0u, event.flags); + EXPECT_EQ(isaGpuVa, event.info.module.load); + + auto elf = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].ptr; + auto elfSize = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].dataSize; + EXPECT_EQ(elf, event.info.module.moduleBegin); + EXPECT_EQ(elf + elfSize, event.info.module.moduleEnd); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenVmBindCreateAndDestroyEventsForIsaWhenReadingEventsThenModuleLoadAndUnloadEventsReturned) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + session->handleEvent(&vmBindIsa->base); + + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + session->handleEvent(&vmBindIsa->base); + + auto &isaMap = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap; + EXPECT_EQ(0u, isaMap.size()); + + zet_debug_event_t event = {}; + ze_result_t result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_LOAD, event.type); + EXPECT_EQ(isaGpuVa, event.info.module.load); + + memset(&event, 0, sizeof(zet_debug_event_t)); + result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_UNLOAD, event.type); + + EXPECT_EQ(isaGpuVa, event.info.module.load); + + auto elf = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].ptr; + auto elfSize = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].dataSize; + EXPECT_EQ(elf, event.info.module.moduleBegin); + EXPECT_EQ(elf + elfSize, event.info.module.moduleEnd); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenIsaBoundMultipleTimesWhenHandlingVmBindDestroyEventThenModuleUnloadEventIsNotPushedToQueue) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + auto &isaMap = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap; + + auto isa = std::make_unique(); + isa->bindInfo = {isaGpuVa, isaSize}; + isa->vmHandle = 3; + isa->elfUuidHandle = DebugSessionLinux::invalidHandle; + isa->moduleBegin = 0; + isa->moduleEnd = 0; + isa->vmBindCounter = 5; + isaMap[isaGpuVa] = std::move(isa); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(1u, isaMap.size()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenVmBindEventForIsaWithInvalidElfWhenReadingEventThenModuleLoadEventReturnZeroElf) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 3; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[3]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID + 1000); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + zet_debug_event_t event = {}; + ze_result_t result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_LOAD, event.type); + + EXPECT_EQ(isaGpuVa, event.info.module.load); + EXPECT_EQ(0u, event.info.module.moduleBegin); + EXPECT_EQ(0u, event.info.module.moduleEnd); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenEventWithL0ZebinModuleWhenHandlingEventThenModuleLoadAndUnloadEventsAreReportedForLastKernel) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaGpuVa2 = 0x340000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + const uint32_t kernelCount = 2; + DebugSessionLinux::UuidData zebinModuleUuidData = { + .handle = zebinModuleUUID, + .classHandle = zebinModuleClassHandle, + .classIndex = NEO::DrmResourceClass::L0ZebinModule, + .data = std::make_unique(sizeof(kernelCount)), + .dataSize = sizeof(kernelCount)}; + + memcpy(zebinModuleUuidData.data.get(), &kernelCount, sizeof(kernelCount)); + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[zebinModuleClassHandle] = {"L0_ZEBIN_MODULE", static_cast(NEO::DrmResourceClass::L0ZebinModule)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(zebinModuleUUID, std::move(zebinModuleUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].segmentCount = 2; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].ptr = 0x1000; + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 4; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[4]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + uuidsTemp[3] = static_cast(zebinModuleUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].loadAddresses.size()); + + vmBindIsa->va_start = isaGpuVa2; + + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.size()); + EXPECT_EQ(2u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].loadAddresses.size()); + EXPECT_EQ(2u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].segmentCount); + + auto &isaMap = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap; + EXPECT_EQ(2u, isaMap.size()); + + zet_debug_event_t event = {}; + ze_result_t result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_LOAD, event.type); + EXPECT_EQ(isaGpuVa2, event.info.module.load); + + auto elfAddress = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].ptr; + auto elfSize = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].dataSize; + EXPECT_EQ(elfAddress, event.info.module.moduleBegin); + EXPECT_EQ(elfAddress + elfSize, event.info.module.moduleEnd); + + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + vmBindIsa->va_start = isaGpuVa2; + + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + vmBindIsa->va_start = isaGpuVa; + + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + + memset(&event, 0, sizeof(zet_debug_event_t)); + + result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_UNLOAD, event.type); + EXPECT_EQ(isaGpuVa2, event.info.module.load); + EXPECT_EQ(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].ptr, event.info.module.moduleBegin); + EXPECT_EQ(session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].ptr + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].dataSize, + event.info.module.moduleEnd); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenMultipleSegmentsInL0ZebinModuleWhenLoadAddressCountIsNotEqualSegmentsCountThenModuleLoadEventIsNotTriggered) { + const uint64_t isaGpuVa = 0x345000; + const uint64_t dataGpuVa = isaGpuVa; // the same load address used, error condition, no module load event triggered + const uint64_t isaSize = 0x2000; + const uint64_t dataSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + const uint32_t segmentCount = 2; + DebugSessionLinux::UuidData zebinModuleUuidData = { + .handle = zebinModuleUUID, + .classHandle = zebinModuleClassHandle, + .classIndex = NEO::DrmResourceClass::L0ZebinModule, + .data = std::make_unique(sizeof(segmentCount)), + .dataSize = sizeof(segmentCount)}; + + memcpy(zebinModuleUuidData.data.get(), &segmentCount, sizeof(segmentCount)); + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[zebinModuleClassHandle] = {"L0_ZEBIN_MODULE", static_cast(NEO::DrmResourceClass::L0ZebinModule)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(zebinModuleUUID, std::move(zebinModuleUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].segmentCount = segmentCount; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[elfUUID].ptr = 0x1000; + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 4; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[4]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + uuidsTemp[3] = static_cast(zebinModuleUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + session->handleEvent(&vmBindIsa->base); + + vmBindIsa->num_uuids = 1; + vmBindIsa->va_start = dataGpuVa; + vmBindIsa->va_length = dataSize; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 1 * sizeof(typeOfUUID); + uuidsTemp[0] = static_cast(zebinModuleUUID); + + memcpy(uuids, uuidsTemp, sizeof(typeOfUUID)); + + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); +} + +TEST_F(DebugApiLinuxVmBindTest, GivenMultipleBindEventsWithZebinModuleWhenHandlingEventsThenModuleLoadIsReportedOnceOnly) { + uint64_t isaGpuVa = 0x345000; + uint64_t isaSize = 0x2000; + uint64_t vmBindIsaData[sizeof(prelim_drm_i915_debug_event_vm_bind) / sizeof(uint64_t) + 3 * sizeof(typeOfUUID)]; + prelim_drm_i915_debug_event_vm_bind *vmBindIsa = reinterpret_cast(&vmBindIsaData); + + const uint32_t kernelCount = 2; + DebugSessionLinux::UuidData zebinModuleUuidData = { + .handle = zebinModuleUUID, + .classHandle = zebinModuleClassHandle, + .classIndex = NEO::DrmResourceClass::L0ZebinModule, + .data = std::make_unique(sizeof(kernelCount)), + .dataSize = sizeof(kernelCount)}; + + memcpy(zebinModuleUuidData.data.get(), &kernelCount, sizeof(kernelCount)); + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->classHandleToIndex[zebinModuleClassHandle] = {"L0_ZEBIN_MODULE", static_cast(NEO::DrmResourceClass::L0ZebinModule)}; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap.emplace(zebinModuleUUID, std::move(zebinModuleUuidData)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].segmentCount = 1; + + vmBindIsa->base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND; + vmBindIsa->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmBindIsa->base.size = sizeof(prelim_drm_i915_debug_event_vm_bind) + 3 * sizeof(typeOfUUID); + vmBindIsa->client_handle = MockDebugSessionLinux::mockClientHandle; + vmBindIsa->va_start = isaGpuVa; + vmBindIsa->va_length = isaSize; + vmBindIsa->vm_handle = vmHandleForVmBind; + vmBindIsa->num_uuids = 4; + auto *uuids = reinterpret_cast(ptrOffset(vmBindIsaData, sizeof(prelim_drm_i915_debug_event_vm_bind))); + typeOfUUID uuidsTemp[4]; + uuidsTemp[0] = static_cast(isaUUID); + uuidsTemp[1] = static_cast(cookieUUID); + uuidsTemp[2] = static_cast(elfUUID); + uuidsTemp[3] = static_cast(zebinModuleUUID); + + memcpy(uuids, uuidsTemp, sizeof(uuidsTemp)); + + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].loadAddresses.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].segmentCount); + + vmBindIsa->vm_handle = vmHandleForVmBind + 1000; + session->handleEvent(&vmBindIsa->base); + + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].loadAddresses.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidToModule[zebinModuleUUID].segmentCount); + + auto &isaMap = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->isaMap; + EXPECT_EQ(1u, isaMap.size()); + + zet_debug_event_t event = {}; + ze_result_t result = zetDebugReadEvent(session->toHandle(), 0, &event); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_LOAD, event.type); + EXPECT_EQ(isaGpuVa, event.info.module.load); +} + +TEST_F(DebugApiLinuxTest, GivenVmEventWithCreateAndDestroyFlagsWhenHandlingEventThenVmIdIsStoredAndErased) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + prelim_drm_i915_debug_event_vm vmEvent = {}; + vmEvent.base.type = PRELIM_DRM_I915_DEBUG_EVENT_VM; + vmEvent.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + vmEvent.base.size = sizeof(prelim_drm_i915_debug_event_vm); + vmEvent.client_handle = MockDebugSessionLinux::mockClientHandle; + vmEvent.handle = 4; + + session->handleEvent(&vmEvent.base); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.size()); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.count(4)); + + vmEvent.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + session->handleEvent(&vmEvent.base); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.size()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.count(4)); +} + +TEST_F(DebugApiLinuxTest, GivenNonClassUuidEventWithoutPayloadWhenHandlingEventThenReadEventIoctlIsNotCalledAndUuidIsSaved) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + prelim_drm_i915_debug_event_uuid uuid = {}; + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + uuid.client_handle = MockDebugSessionLinux::mockClientHandle; + uuid.handle = 2; + uuid.payload_size = 0; + uuid.class_handle = 50; + + const char uuidString[] = "00000000-0000-0000-0000-000000000001"; + + prelim_drm_i915_debug_read_uuid readUuid = {}; + readUuid.client_handle = MockDebugSessionLinux::mockClientHandle; + memcpy(readUuid.uuid, uuidString, 36); + readUuid.payload_ptr = 0; + readUuid.payload_size = 0; + readUuid.handle = 2; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + handler->returnUuid = &readUuid; + + session->handleEvent(reinterpret_cast(&uuid)); + + EXPECT_EQ(50u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuid.handle].classHandle); + EXPECT_EQ(uuid.handle, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuid.handle].handle); + EXPECT_EQ(uuid.class_handle, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuid.handle].classHandle); + EXPECT_EQ(nullptr, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuid.handle].data); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->uuidMap[uuid.handle].dataSize); + + EXPECT_EQ(0, handler->ioctlCalled); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerLogsAndFailingReadUuidEventIoctlWhenHandlingEventThenErrorIsPrinted) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_ERROR); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto uuidName = NEO::classNamesToUuid[0].first; + auto uuidHash = NEO::classNamesToUuid[0].second; + auto uuidNameSize = strlen(uuidName); + prelim_drm_i915_debug_event_uuid uuid = {}; + + uuid.base.type = PRELIM_DRM_I915_DEBUG_EVENT_UUID; + uuid.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + uuid.base.size = sizeof(prelim_drm_i915_debug_event_uuid); + + uuid.client_handle = 1; + uuid.handle = 2; + uuid.payload_size = uuidNameSize; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + ::testing::internal::CaptureStderr(); + errno = 0; + session->handleEvent(reinterpret_cast(&uuid)); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(0u, session->getClassHandleToIndex().size()); + + auto errorMessage = ::testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string("\nERROR: PRELIM_I915_DEBUG_IOCTL_READ_UUID res = -1 errno = 0\n"), errorMessage); +} + +TEST_F(DebugApiLinuxTest, GivenEventsAvailableWhenReadingEventThenEventsAreReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + zet_debug_event_t debugEvent = {}; + debugEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.push(debugEvent); + + zet_debug_event_t debugEvent2 = {}; + debugEvent2.type = ZET_DEBUG_EVENT_TYPE_MODULE_LOAD; + debugEvent2.info.module.format = ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF; + debugEvent2.info.module.moduleBegin = 1000; + debugEvent2.info.module.moduleEnd = 2000; + debugEvent2.info.module.load = 0x1234000; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.push(debugEvent2); + + zet_debug_event_t debugEvent3 = {}; + debugEvent3.type = ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.push(debugEvent3); + + zet_debug_event_t outputEvent = {}; + + auto result = session->readEvent(10, &outputEvent); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY, outputEvent.type); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + result = session->readEvent(10, &outputEvent); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_MODULE_LOAD, outputEvent.type); + EXPECT_EQ(ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF, outputEvent.info.module.format); + EXPECT_EQ(1000u, outputEvent.info.module.moduleBegin); + EXPECT_EQ(0x1234000u, outputEvent.info.module.load); + + result = session->readEvent(10, &outputEvent); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT, outputEvent.type); +} + +TEST_F(DebugApiLinuxTest, GivenContextParamEventWhenTypeIsParamVmThenVmIdIsStoredForContext) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + uint64_t vmId = 10; + uint64_t contextHandle = 20; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[contextHandle].handle = contextHandle; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.insert(vmId); + + prelim_drm_i915_debug_event_context_param contextParamEvent = {}; + contextParamEvent.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT_PARAM; + contextParamEvent.base.flags = 0; + contextParamEvent.base.size = sizeof(prelim_drm_i915_debug_event_context_param); + contextParamEvent.client_handle = MockDebugSessionLinux::mockClientHandle; + contextParamEvent.ctx_handle = contextHandle; + contextParamEvent.param = {.ctx_id = 3, .size = 8, .param = I915_CONTEXT_PARAM_VM, .value = vmId}; + + session->handleEvent(&contextParamEvent.base); + EXPECT_EQ(vmId, session->clientHandleToConnection[contextParamEvent.client_handle]->contextsCreated[contextHandle].vm); +} + +TEST_F(DebugApiLinuxTest, GivenContextParamEventWhenTypeIsParamEngineThenEventIsHandled) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_INFO); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + uint64_t vmId = 10; + uint32_t contextHandle = 20; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[contextHandle].handle = contextHandle; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.insert(vmId); + + constexpr auto size = sizeof(prelim_drm_i915_debug_event_context_param) + sizeof(i915_context_param_engines) + sizeof(i915_engine_class_instance); + + auto memory = alignedMalloc(size, sizeof(prelim_drm_i915_debug_event_context_param)); + auto *contextParamEvent = static_cast(memory); + + { + contextParamEvent->base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT_PARAM; + contextParamEvent->base.flags = 0; + contextParamEvent->base.size = size; + contextParamEvent->client_handle = MockDebugSessionLinux::mockClientHandle; + contextParamEvent->ctx_handle = contextHandle; + } + + auto offset = offsetof(prelim_drm_i915_debug_event_context_param, param); + + drm_i915_gem_context_param paramToCopy = {}; + paramToCopy.ctx_id = contextHandle; + paramToCopy.size = sizeof(i915_context_param_engines) + sizeof(i915_engine_class_instance); + paramToCopy.param = I915_CONTEXT_PARAM_ENGINES; + paramToCopy.value = 0; + memcpy(ptrOffset(memory, offset), ¶mToCopy, sizeof(drm_i915_gem_context_param)); + + auto valueOffset = offsetof(drm_i915_gem_context_param, value); + auto *engines = ptrOffset(memory, offset + valueOffset); + i915_context_param_engines enginesParam; + enginesParam.extensions = 0; + memcpy(engines, &enginesParam, sizeof(i915_context_param_engines)); + + auto enginesOffset = offsetof(i915_context_param_engines, engines); + auto *classInstance = ptrOffset(memory, offset + valueOffset + enginesOffset); + i915_engine_class_instance ci = {I915_ENGINE_CLASS_RENDER, 1}; + memcpy(classInstance, &ci, sizeof(i915_engine_class_instance)); + + ::testing::internal::CaptureStdout(); + session->handleEvent(&contextParamEvent->base); + alignedFree(memory); + + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[contextHandle].engines.size()); + EXPECT_EQ(static_cast(I915_ENGINE_CLASS_RENDER), session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[contextHandle].engines[0].engine_class); + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[contextHandle].engines[0].engine_instance); + + auto infoMessage = ::testing::internal::GetCapturedStdout(); + EXPECT_THAT(infoMessage, testing::HasSubstr(std::string("I915_CONTEXT_PARAM_ENGINES ctx_id = 20 param = 10 value = 0"))); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerErrorLogsWhenContextParamWithInvalidContextIsHandledThenErrorIsPrinted) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_ERROR); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + uint64_t vmId = 10; + uint64_t contextHandle = 20; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[contextHandle].handle = contextHandle; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.insert(vmId); + + prelim_drm_i915_debug_event_context_param contextParamEvent = {}; + contextParamEvent.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT_PARAM; + contextParamEvent.base.flags = 0; + contextParamEvent.base.size = sizeof(prelim_drm_i915_debug_event_context_param); + contextParamEvent.client_handle = MockDebugSessionLinux::mockClientHandle; + contextParamEvent.ctx_handle = 77; + contextParamEvent.param = {.ctx_id = 3, .size = 8, .param = I915_CONTEXT_PARAM_VM, .value = vmId}; + + ::testing::internal::CaptureStderr(); + session->handleEvent(&contextParamEvent.base); + + EXPECT_EQ(session->clientHandleToConnection[contextParamEvent.client_handle]->contextsCreated.end(), + session->clientHandleToConnection[contextParamEvent.client_handle]->contextsCreated.find(77)); + + auto errorMessage = ::testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string("\nERROR: CONTEXT handle does not exist\n"), errorMessage); +} + +TEST_F(DebugApiLinuxTest, GivenDebuggerInfoLogsWhenHandlingContextParamEventWithUnknownParamThenInfoIsPrinted) { + DebugManagerStateRestore restorer; + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_INFO); + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = DebugSessionLinux::invalidClientHandle; + + uint64_t vmId = 10; + uint64_t contextHandle = 20; + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[contextHandle].handle = contextHandle; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmIds.insert(vmId); + + prelim_drm_i915_debug_event_context_param contextParamEvent = {}; + contextParamEvent.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CONTEXT_PARAM; + contextParamEvent.base.flags = 0; + contextParamEvent.base.size = sizeof(prelim_drm_i915_debug_event_context_param); + contextParamEvent.client_handle = MockDebugSessionLinux::mockClientHandle; + contextParamEvent.ctx_handle = contextHandle; + contextParamEvent.param = {.ctx_id = 3, .size = 8, .param = I915_CONTEXT_PARAM_BAN_PERIOD, .value = vmId}; + + ::testing::internal::CaptureStdout(); + session->handleEvent(&contextParamEvent.base); + + EXPECT_EQ(session->clientHandleToConnection[contextParamEvent.client_handle]->contextsCreated.end(), + session->clientHandleToConnection[contextParamEvent.client_handle]->contextsCreated.find(77)); + + auto errorMessage = ::testing::internal::GetCapturedStdout(); + EXPECT_THAT(errorMessage, testing::HasSubstr(std::string("client_handle = 1 ctx_handle = 20\n\nINFO: I915_CONTEXT_PARAM UNHANDLED = 1\n"))); +} + +TEST_F(DebugApiLinuxTest, WhenCallingThreadControlForInterruptThenProperIoctlsIsCalled) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + std::vector threads({}); + + std::unique_ptr bitmaskOut; + size_t bitmaskSizeOut = 0; + + auto result = sessionMock->threadControl(threads, 0, MockDebugSessionLinux::ThreadControlCmd::Interrupt, bitmaskOut, bitmaskSizeOut); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT), handler->euControl.cmd); + EXPECT_NE(0u, handler->euControl.bitmask_size); + EXPECT_NE(0u, handler->euControl.bitmask_ptr); + EXPECT_EQ(handler->euControlOutputSeqno, sessionMock->euControlInterruptSeqno[0]); + + EXPECT_EQ(0u, bitmaskSizeOut); + EXPECT_EQ(nullptr, bitmaskOut.get()); + + handler->euControlOutputSeqno = handler->euControlOutputSeqno + 3; + handler->ioctlCalled = 0; + result = sessionMock->threadControl(threads, 0, MockDebugSessionLinux::ThreadControlCmd::InterruptAll, bitmaskOut, bitmaskSizeOut); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT_ALL), handler->euControl.cmd); + EXPECT_EQ(0u, handler->euControl.bitmask_size); + EXPECT_EQ(0u, handler->euControl.bitmask_ptr); + EXPECT_EQ(handler->euControlOutputSeqno, sessionMock->euControlInterruptSeqno[0]); + + EXPECT_EQ(0u, bitmaskSizeOut); + EXPECT_EQ(nullptr, bitmaskOut.get()); +} + +TEST_F(DebugApiLinuxTest, GivenErrorFromIoctlWhenCallingThreadControlForInterruptThenSeqnoIsNotUpdated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + std::vector threads({}); + + std::unique_ptr bitmaskOut; + size_t bitmaskSizeOut = 0; + + handler->ioctlRetVal = -1; + + auto result = sessionMock->threadControl(threads, 0, MockDebugSessionLinux::ThreadControlCmd::InterruptAll, bitmaskOut, bitmaskSizeOut); + EXPECT_NE(0, result); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_INTERRUPT_ALL), handler->euControl.cmd); + EXPECT_NE(handler->euControlOutputSeqno, sessionMock->euControlInterruptSeqno[0]); +} + +TEST_F(DebugApiLinuxTest, WhenCallingThreadControlForResumeThenProperIoctlsIsCalled) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + std::vector threads({}); + + std::unique_ptr bitmaskOut; + size_t bitmaskSizeOut = 0; + + auto result = sessionMock->threadControl(threads, 0, MockDebugSessionLinux::ThreadControlCmd::Resume, bitmaskOut, bitmaskSizeOut); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_RESUME), handler->euControl.cmd); + EXPECT_NE(0u, handler->euControl.bitmask_size); + EXPECT_NE(0u, handler->euControl.bitmask_ptr); + + EXPECT_EQ(0u, bitmaskSizeOut); + EXPECT_EQ(nullptr, bitmaskOut.get()); +} + +TEST_F(DebugApiLinuxTest, GivenResumeWARequiredWhenCallingResumeThenWaIsAppliedToBitmask) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto &l0HwHelper = L0HwHelper::get(neoDevice->getHardwareInfo().platform.eRenderCoreFamily); + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + SIP::version version = {2, 0, 0}; + initStateSaveArea(sessionMock->stateSaveAreaHeader, version); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + ze_device_thread_t thread = {0, 0, 0, 0}; + sessionMock->allThreads[EuThread::ThreadId(0, thread)]->stopThread(1u); + + auto result = sessionMock->resume(thread); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_RESUME), handler->euControl.cmd); + EXPECT_NE(0u, handler->euControl.bitmask_size); + EXPECT_NE(0u, handler->euControl.bitmask_ptr); + + auto bitmask = handler->euControlBitmask.get(); + + EXPECT_EQ(1u, bitmask[0]); + if (l0HwHelper.isResumeWARequired()) { + EXPECT_EQ(1u, bitmask[4]); + } else { + EXPECT_EQ(0u, bitmask[4]); + } + + thread = {0, 0, 4, 0}; + sessionMock->allThreads[EuThread::ThreadId(0, thread)]->stopThread(1u); + + result = sessionMock->resume(thread); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + bitmask = handler->euControlBitmask.get(); + + if (l0HwHelper.isResumeWARequired()) { + EXPECT_EQ(1u, bitmask[0]); + EXPECT_EQ(1u, bitmask[4]); + } else { + EXPECT_EQ(0u, bitmask[0]); + EXPECT_EQ(1u, bitmask[4]); + } +} + +TEST_F(DebugApiLinuxTest, GivenSliceALLWhenCallingResumeThenSliceIdIsNotRemapped) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + ze_device_thread_t thread = {}; + thread.slice = UINT32_MAX; + + for (uint32_t i = 0; i < device->getHwInfo().gtSystemInfo.MaxSlicesSupported; i++) { + ze_device_thread_t singleThread = thread; + singleThread.slice = i; + sessionMock->allThreads[EuThread::ThreadId(0, singleThread)]->stopThread(1u); + } + + auto result = sessionMock->resume(thread); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_RESUME), handler->euControl.cmd); + EXPECT_EQ(device->getHwInfo().gtSystemInfo.MaxSlicesSupported, sessionMock->numThreadsPassedToThreadControl); +} + +TEST_F(DebugApiLinuxTest, GivenErrorFromIoctlWhenCallingResumeThenErrorUnknownIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + handler->ioctlRetVal = -1; + + ze_device_thread_t thread = {}; + sessionMock->allThreads[EuThread::ThreadId(0, thread)]->stopThread(1u); + + auto result = sessionMock->resume(thread); + EXPECT_EQ(result, ZE_RESULT_ERROR_UNKNOWN); +} + +TEST_F(DebugApiLinuxTest, GivenErrorFromIoctlWhenCallingInterruptImpThenErrorNotAvailableIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + handler->ioctlRetVal = -1; + + auto result = sessionMock->interruptImp(0); + EXPECT_EQ(result, ZE_RESULT_ERROR_NOT_AVAILABLE); +} + +TEST_F(DebugApiLinuxTest, WhenCallingThreadControlForThreadStoppedThenProperIoctlsIsCalled) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + + std::vector threads({}); + + std::unique_ptr bitmaskOut; + size_t bitmaskSizeOut = 0; + + auto result = sessionMock->threadControl(threads, 0, MockDebugSessionLinux::ThreadControlCmd::Stopped, bitmaskOut, bitmaskSizeOut); + EXPECT_EQ(result, ZE_RESULT_SUCCESS); + + EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_STOPPED), handler->euControl.cmd); + + EXPECT_NE(0u, bitmaskSizeOut); + EXPECT_NE(nullptr, bitmaskOut.get()); +} + +TEST_F(DebugApiLinuxTest, givenEnginesEventHandledThenLrcToContextHandleMapIsFilled) { + DebugManagerStateRestore restorer; + + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t clientHandle = 34; + prelim_drm_i915_debug_event_client client = {}; + client.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client.base.size = sizeof(prelim_drm_i915_debug_event_client); + client.handle = clientHandle; + session->handleEvent(&client.base); + + unsigned char bytes1[sizeof(prelim_drm_i915_debug_event_engines) + 2 * sizeof(prelim_drm_i915_debug_engine_info)]; + prelim_drm_i915_debug_event_engines *engines1 = reinterpret_cast(bytes1); + engines1->base.type = PRELIM_DRM_I915_DEBUG_EVENT_ENGINES; + engines1->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + engines1->client_handle = clientHandle; + engines1->ctx_handle = 20; + engines1->num_engines = 2; + engines1->engines[0].lrc_handle = 1; + engines1->engines[1].lrc_handle = 2; + + unsigned char bytes2[sizeof(prelim_drm_i915_debug_event_engines) + 4 * sizeof(prelim_drm_i915_debug_engine_info)]; + prelim_drm_i915_debug_event_engines *engines2 = reinterpret_cast(bytes2); + engines2->base.type = PRELIM_DRM_I915_DEBUG_EVENT_ENGINES; + engines2->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + engines2->client_handle = clientHandle; + engines2->ctx_handle = 40; + engines2->num_engines = 4; + engines2->engines[0].lrc_handle = 3; + engines2->engines[1].lrc_handle = 4; + engines2->engines[2].lrc_handle = 5; + engines2->engines[3].lrc_handle = 0; + + ::testing::internal::CaptureStdout(); + NEO::DebugManager.flags.DebuggerLogBitmask.set(NEO::DebugVariables::DEBUGGER_LOG_BITMASK::LOG_INFO); + + session->handleEvent(&engines1->base); + session->handleEvent(&engines2->base); + + EXPECT_EQ(6u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle.size()); + EXPECT_EQ(20u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[1]); + EXPECT_EQ(20u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[2]); + EXPECT_EQ(40u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[3]); + EXPECT_EQ(40u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[4]); + EXPECT_EQ(40u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[5]); + + engines1->base.flags = PRELIM_DRM_I915_DEBUG_EVENT_DESTROY; + session->handleEvent(&engines1->base); + EXPECT_EQ(4u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle.size()); + EXPECT_EQ(session->clientHandleToConnection[clientHandle]->lrcToContextHandle.find(1), session->clientHandleToConnection[clientHandle]->lrcToContextHandle.end()); + EXPECT_EQ(session->clientHandleToConnection[clientHandle]->lrcToContextHandle.find(2), session->clientHandleToConnection[clientHandle]->lrcToContextHandle.end()); + EXPECT_EQ(40u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[3]); + EXPECT_EQ(40u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[4]); + EXPECT_EQ(40u, session->clientHandleToConnection[clientHandle]->lrcToContextHandle[5]); + + auto infoMessage = ::testing::internal::GetCapturedStdout(); + EXPECT_THAT(infoMessage, testing::HasSubstr(std::string("ENGINES event: client_handle = 34, ctx_handle = 20, num_engines = 2 CREATE"))); + EXPECT_THAT(infoMessage, testing::HasSubstr(std::string("ENGINES event: client_handle = 34, ctx_handle = 40, num_engines = 4 CREATE"))); + EXPECT_THAT(infoMessage, testing::HasSubstr(std::string("ENGINES event: client_handle = 34, ctx_handle = 20, num_engines = 2 DESTROY"))); +} + +using DebugApiLinuxAttentionTest = Test; + +TEST_F(DebugApiLinuxAttentionTest, GivenEuAttentionEventForThreadsWhenHandlingEventThenNewlyStoppedThreadsSaved) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t ctxHandle = 2; + uint64_t vmHandle = 7; + uint64_t lrcHandle = 8; + + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->lrcToContextHandle[lrcHandle] = ctxHandle; + + uint8_t data[sizeof(prelim_drm_i915_debug_event_eu_attention) + 128]; + std::unique_ptr bitmask; + size_t bitmaskSize = 0; + auto &hwInfo = neoDevice->getHardwareInfo(); + auto &l0HwHelper = L0HwHelper::get(hwInfo.platform.eRenderCoreFamily); + + std::vector threads{ + {0, 0, 0, 0}, + {0, 0, 0, 1}, + {0, 0, 0, 2}, + {0, 0, 0, 3}, + {0, 0, 0, 4}, + {0, 0, 0, 5}, + {0, 0, 0, 6}}; + + for (auto thread : threads) { + sessionMock->stoppedThreads[EuThread::ThreadId(0, thread).packed] = 1; + } + + l0HwHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize); + + prelim_drm_i915_debug_event_eu_attention attention = {}; + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention) + std::min(uint32_t(128), static_cast(bitmaskSize)); + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.lrc_handle = lrcHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = std::min(uint32_t(128), static_cast(bitmaskSize)); + + memcpy(data, &attention, sizeof(prelim_drm_i915_debug_event_eu_attention)); + memcpy(ptrOffset(data, offsetof(prelim_drm_i915_debug_event_eu_attention, bitmask)), bitmask.get(), std::min(size_t(128), bitmaskSize)); + + sessionMock->handleEvent(reinterpret_cast(data)); + + EXPECT_EQ(threads.size(), sessionMock->newlyStoppedThreads.size()); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenEuAttentionEventWithInvalidClientWhenHandlingEventThenNoStoppedThreadsSet) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint8_t data[sizeof(prelim_drm_i915_debug_event_eu_attention) + 128]; + std::unique_ptr bitmask; + size_t bitmaskSize = 0; + auto &hwInfo = neoDevice->getHardwareInfo(); + auto &l0HwHelper = L0HwHelper::get(hwInfo.platform.eRenderCoreFamily); + + std::vector threads{ + {0, 0, 0, 0}, + {0, 0, 0, 1}, + {0, 0, 0, 2}, + {0, 0, 0, 3}, + {0, 0, 0, 4}, + {0, 0, 0, 5}, + {0, 0, 0, 6}}; + + l0HwHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize); + + prelim_drm_i915_debug_event_eu_attention attention = {}; + + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention) + std::min(uint32_t(128), static_cast(bitmaskSize)); + + attention.client_handle = MockDebugSessionLinux::invalidClientHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = std::min(uint32_t(128), static_cast(bitmaskSize)); + + memcpy(data, &attention, sizeof(prelim_drm_i915_debug_event_eu_attention)); + memcpy(ptrOffset(data, offsetof(prelim_drm_i915_debug_event_eu_attention, bitmask)), bitmask.get(), std::min(size_t(128), bitmaskSize)); + + sessionMock->handleEvent(reinterpret_cast(data)); + + EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); + EXPECT_FALSE(sessionMock->triggerEvents); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenEuAttentionEventEmptyBitmaskWhenHandlingEventThenNoStoppedThreadsSetAndTriggerEventsFalse) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t ctxHandle = 2; + uint64_t vmHandle = 7; + uint64_t lrcHandle = 8; + + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->lrcToContextHandle[lrcHandle] = ctxHandle; + + uint8_t data[sizeof(prelim_drm_i915_debug_event_eu_attention) + sizeof(uint64_t)]; + prelim_drm_i915_debug_event_eu_attention attention = {}; + + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention) + sizeof(uint64_t); + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.lrc_handle = lrcHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = sizeof(uint64_t); + + uint64_t bitmask = 0; + + memcpy(data, &attention, sizeof(prelim_drm_i915_debug_event_eu_attention)); + memcpy(ptrOffset(data, offsetof(prelim_drm_i915_debug_event_eu_attention, bitmask)), &bitmask, sizeof(uint64_t)); + + sessionMock->handleEvent(reinterpret_cast(data)); + + EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); + EXPECT_FALSE(sessionMock->triggerEvents); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenInterruptedThreadsWhenOnlySomeThreadsRaisesAttentionThenPendingInterruptsAreMarked) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t ctxHandle = 2; + uint64_t vmHandle = 7; + uint64_t lrcHandle = 8; + + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->lrcToContextHandle[lrcHandle] = ctxHandle; + + uint8_t data[sizeof(prelim_drm_i915_debug_event_eu_attention) + 128]; + std::unique_ptr bitmask; + size_t bitmaskSize = 0; + auto &hwInfo = neoDevice->getHardwareInfo(); + auto &l0HwHelper = L0HwHelper::get(hwInfo.platform.eRenderCoreFamily); + + std::vector threads{ + {0, 0, 0, 0}}; + + sessionMock->stoppedThreads[EuThread::ThreadId(0, threads[0]).packed] = 1; + + if (hwInfo.gtSystemInfo.MaxEuPerSubSlice > 8) { + sessionMock->allThreads[EuThread::ThreadId(0, 0, 0, 4, 0)]->stopThread(1u); + } + + l0HwHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize); + + ze_device_thread_t thread = {0, 0, 0, UINT32_MAX}; + ze_device_thread_t thread2 = {0, 0, 1, UINT32_MAX}; + sessionMock->pendingInterrupts.push_back(std::pair(thread, false)); + sessionMock->pendingInterrupts.push_back(std::pair(thread2, false)); + sessionMock->interruptSent = true; + sessionMock->euControlInterruptSeqno[0] = 1; + + prelim_drm_i915_debug_event_eu_attention attention = {}; + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention) + std::min(uint32_t(128), static_cast(bitmaskSize)); + attention.base.seqno = 2; + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.lrc_handle = lrcHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = std::min(uint32_t(128), static_cast(bitmaskSize)); + + memcpy(data, &attention, sizeof(prelim_drm_i915_debug_event_eu_attention)); + memcpy(ptrOffset(data, offsetof(prelim_drm_i915_debug_event_eu_attention, bitmask)), bitmask.get(), std::min(size_t(128), bitmaskSize)); + + sessionMock->handleEvent(reinterpret_cast(data)); + + EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); + EXPECT_TRUE(sessionMock->pendingInterrupts[0].second); + EXPECT_FALSE(sessionMock->pendingInterrupts[1].second); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenInterruptedThreadsWhenAttentionEventReceivedThenEventsTriggeredAfterExpectedAttentionEventCount) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t ctxHandle = 2; + uint64_t vmHandle = 7; + uint64_t lrcHandle = 8; + + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->lrcToContextHandle[lrcHandle] = ctxHandle; + + uint8_t data[sizeof(prelim_drm_i915_debug_event_eu_attention) + 128]; + ze_device_thread_t thread{0, 0, 0, 0}; + + sessionMock->stoppedThreads[EuThread::ThreadId(0, thread).packed] = 1; + sessionMock->pendingInterrupts.push_back(std::pair(thread, false)); + + sessionMock->interruptSent = true; + sessionMock->euControlInterruptSeqno[0] = 1; + + prelim_drm_i915_debug_event_eu_attention attention = {}; + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention); + attention.base.seqno = 2; + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.lrc_handle = lrcHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = 0; + + memcpy(data, &attention, sizeof(prelim_drm_i915_debug_event_eu_attention)); + + sessionMock->expectedAttentionEvents = 2; + + sessionMock->handleEvent(reinterpret_cast(data)); + + EXPECT_FALSE(sessionMock->triggerEvents); + + attention.base.seqno = 10; + memcpy(data, &attention, sizeof(prelim_drm_i915_debug_event_eu_attention)); + sessionMock->handleEvent(reinterpret_cast(data)); + + EXPECT_TRUE(sessionMock->triggerEvents); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenEventSeqnoLowerEqualThanSentInterruptWhenHandlingAttentionEventThenEventIsNotProcessed) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint8_t data[sizeof(prelim_drm_i915_debug_event_eu_attention) + 128]; + std::unique_ptr bitmask; + size_t bitmaskSize = 0; + auto &hwInfo = neoDevice->getHardwareInfo(); + auto &l0HwHelper = L0HwHelper::get(hwInfo.platform.eRenderCoreFamily); + + std::vector threads{ + {0, 0, 0, 0}, + {0, 0, 0, 1}, + {0, 0, 0, 2}, + {0, 0, 0, 3}, + {0, 0, 0, 4}, + {0, 0, 0, 5}, + {0, 0, 0, 6}}; + + l0HwHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize); + + ze_device_thread_t thread = {0, 0, 0, UINT32_MAX}; + sessionMock->pendingInterrupts.push_back(std::pair(thread, false)); + sessionMock->interruptSent = true; + sessionMock->euControlInterruptSeqno[0] = 3; + + prelim_drm_i915_debug_event_eu_attention attention = {}; + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE; + attention.base.seqno = 3; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention) + std::min(uint32_t(128), static_cast(bitmaskSize)); + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = std::min(uint32_t(128), static_cast(bitmaskSize)); + + memcpy(data, &attention, sizeof(prelim_drm_i915_debug_event_eu_attention)); + memcpy(ptrOffset(data, offsetof(prelim_drm_i915_debug_event_eu_attention, bitmask)), bitmask.get(), std::min(size_t(128), bitmaskSize)); + + sessionMock->handleEvent(reinterpret_cast(data)); + + EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); + EXPECT_EQ(1u, sessionMock->pendingInterrupts.size()); + EXPECT_FALSE(sessionMock->pendingInterrupts[0].second); + EXPECT_FALSE(sessionMock->triggerEvents); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenLrcToContextMapEmptyWhenHandlingAttentionEventThenEventIsNotProcessed) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t ctxHandle = 2; + uint64_t vmHandle = 7; + uint64_t lrcHandle = 8; + + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + + prelim_drm_i915_debug_event_eu_attention attention = {}; + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention); + attention.base.seqno = 2; + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.lrc_handle = lrcHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = 0; + + sessionMock->handleEvent(&attention.base); + + EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); + EXPECT_EQ(0u, sessionMock->pendingInterrupts.size()); + EXPECT_FALSE(sessionMock->triggerEvents); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenContextHandleNotFoundWhenHandlingAttentionEventThenEventIsNotProcessed) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t ctxHandle = 2; + uint64_t lrcHandle = 8; + + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->lrcToContextHandle[lrcHandle] = ctxHandle; + + prelim_drm_i915_debug_event_eu_attention attention = {}; + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention); + attention.base.seqno = 2; + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.lrc_handle = lrcHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = 0; + + sessionMock->handleEvent(&attention.base); + + EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); + EXPECT_EQ(0u, sessionMock->pendingInterrupts.size()); + EXPECT_FALSE(sessionMock->triggerEvents); +} + +TEST_F(DebugApiLinuxAttentionTest, GivenInvalidVmHandleWhenHandlingAttentionEventThenEventIsNotProcessed) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + sessionMock->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t ctxHandle = 2; + uint64_t vmHandle = DebugSessionLinux::invalidHandle; + uint64_t lrcHandle = 8; + + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + sessionMock->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->lrcToContextHandle[lrcHandle] = ctxHandle; + + prelim_drm_i915_debug_event_eu_attention attention = {}; + attention.base.type = PRELIM_DRM_I915_DEBUG_EVENT_EU_ATTENTION; + attention.base.size = sizeof(prelim_drm_i915_debug_event_eu_attention); + attention.base.seqno = 2; + attention.client_handle = MockDebugSessionLinux::mockClientHandle; + attention.lrc_handle = lrcHandle; + attention.flags = 0; + attention.ci.engine_class = 0; + attention.ci.engine_instance = 0; + attention.bitmask_size = 0; + + sessionMock->handleEvent(&attention.base); + + EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); + EXPECT_EQ(0u, sessionMock->pendingInterrupts.size()); + EXPECT_FALSE(sessionMock->triggerEvents); +} +using DebugApiLinuxAsyncThreadTest = Test; + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenPollReturnsErrorAndEinvalWhenHandlingEventsAsyncThenDetachEventIsGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + handler->pollRetVal = -1; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + std::unique_ptr> event( + reinterpret_cast(malloc(sizeof(prelim_drm_i915_debug_event) + DebugSessionLinux::maxEventSize)), [](prelim_drm_i915_debug_event *ptr) { free(ptr); }); + event->size = DebugSessionLinux::maxEventSize; + event->type = PRELIM_DRM_I915_DEBUG_EVENT_READ; + event->flags = 0; + errno = EINVAL; + + session->handleEventsAsync(event.get()); + + EXPECT_EQ(1u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_DETACHED, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.front().type); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.pop(); + errno = 0; + + session->handleEventsAsync(event.get()); + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenPollReturnsZeroWhenHandlingEventsAsyncThenNoEventIsReadAndGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + handler->pollRetVal = 0; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + std::unique_ptr> event( + reinterpret_cast(malloc(sizeof(prelim_drm_i915_debug_event) + DebugSessionLinux::maxEventSize)), [](prelim_drm_i915_debug_event *ptr) { free(ptr); }); + event->size = DebugSessionLinux::maxEventSize; + event->type = PRELIM_DRM_I915_DEBUG_EVENT_READ; + event->flags = 0; + + session->handleEventsAsync(event.get()); + EXPECT_EQ(0, handler->ioctlCalled); + EXPECT_EQ(0u, handler->debugEventInput.type); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenPollReturnsNonZeroWhenHandlingEventsAsyncThenEventReadIsCalled) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + handler->pollRetVal = 1; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t clientHandle = 2; + prelim_drm_i915_debug_event_client client = {}; + client.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client.base.size = sizeof(prelim_drm_i915_debug_event_client); + client.handle = clientHandle; + + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + + std::unique_ptr> event( + reinterpret_cast(malloc(sizeof(prelim_drm_i915_debug_event) + DebugSessionLinux::maxEventSize)), [](prelim_drm_i915_debug_event *ptr) { free(ptr); }); + event->size = DebugSessionLinux::maxEventSize; + event->type = PRELIM_DRM_I915_DEBUG_EVENT_READ; + event->flags = 0; + + session->handleEventsAsync(event.get()); + + constexpr int clientEventCount = 1; + constexpr int dummyReadEventCount = 1; + + EXPECT_EQ(clientEventCount + dummyReadEventCount, handler->ioctlCalled); + EXPECT_EQ(DebugSessionLinux::maxEventSize, handler->debugEventInput.size); + EXPECT_EQ(static_cast(PRELIM_DRM_I915_DEBUG_EVENT_READ), handler->debugEventInput.type); +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenPollReturnsNonZeroWhenHandlingEventsAsyncThenEventReadIsCalledForAtMost3Times) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandler; + handler->pollRetVal = 1; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + uint64_t clientHandle = 2; + prelim_drm_i915_debug_event_client client = {}; + client.base.type = PRELIM_DRM_I915_DEBUG_EVENT_CLIENT; + client.base.flags = PRELIM_DRM_I915_DEBUG_EVENT_CREATE; + client.base.size = sizeof(prelim_drm_i915_debug_event_client); + client.handle = clientHandle; + + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.size)}); + + std::unique_ptr> event( + reinterpret_cast(malloc(sizeof(prelim_drm_i915_debug_event) + DebugSessionLinux::maxEventSize)), [](prelim_drm_i915_debug_event *ptr) { free(ptr); }); + event->size = DebugSessionLinux::maxEventSize; + event->type = PRELIM_DRM_I915_DEBUG_EVENT_READ; + event->flags = 0; + + session->handleEventsAsync(event.get()); + + EXPECT_EQ(3, handler->ioctlCalled); + EXPECT_EQ(DebugSessionLinux::maxEventSize, handler->debugEventInput.size); + EXPECT_EQ(static_cast(PRELIM_DRM_I915_DEBUG_EVENT_READ), handler->debugEventInput.type); + EXPECT_EQ(clientHandle, session->clientHandleToConnection[clientHandle]->client.handle); +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenDebugSessionWhenStartingAndClosingAsyncThreadThenThreadIsStartedAndFinishes) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + session->startAsyncThread(); + + while (handler->pollCounter == 0) + ; + + EXPECT_TRUE(session->asyncThreadActive); + EXPECT_FALSE(session->asyncThreadFinished); + + session->closeAsyncThread(); + + EXPECT_FALSE(session->asyncThreadActive); + EXPECT_TRUE(session->asyncThreadFinished); +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenDebugSessionWithAsyncThreadWhenClosingConnectionThenAsyncThreadIsTerminated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + + session->startAsyncThread(); + + while (handler->pollCounter == 0) + ; + + EXPECT_TRUE(session->asyncThreadActive); + EXPECT_FALSE(session->asyncThreadFinished); + + session->closeConnection(); + + EXPECT_FALSE(session->asyncThreadActive); + EXPECT_TRUE(session->asyncThreadFinished); +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenNoEventsAvailableWithinTimeoutWhenReadingEventThenNotReadyReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + zet_debug_event_t outputEvent = {}; + outputEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY; + + auto result = session->readEvent(10, &outputEvent); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_INVALID, outputEvent.type); + EXPECT_EQ(0u, outputEvent.flags); + EXPECT_EQ(ZE_RESULT_NOT_READY, result); +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenInterruptedThreadsWhenNoAttentionEventIsReadThenThreadUnavailableEventIsGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->returnTimeDiff = DebugSessionLinux::interruptTimeout * 10; + + session->startAsyncThread(); + + ze_device_thread_t thread = {0, 0, 0, UINT32_MAX}; + auto result = session->interrupt(thread); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + while (handler->pollCounter == 0) + ; + + session->closeAsyncThread(); + + if (session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size() > 0) { + auto event = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.front(); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_THREAD_UNAVAILABLE, event.type); + EXPECT_EQ(0u, event.info.thread.thread.slice); + EXPECT_EQ(0u, event.info.thread.thread.subslice); + EXPECT_EQ(0u, event.info.thread.thread.eu); + EXPECT_EQ(UINT32_MAX, event.info.thread.thread.thread); + } +} + +TEST_F(DebugApiLinuxAsyncThreadTest, GivenInterruptedThreadsWhenNoAttentionEventIsReadWithinTimeoutThenNoEventGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + + auto handler = new MockIoctlHandler; + session->ioctlHandler.reset(handler); + session->returnTimeDiff = 0; + + session->startAsyncThread(); + + ze_device_thread_t thread = {0, 0, 0, UINT32_MAX}; + auto result = session->interrupt(thread); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + while (handler->pollCounter == 0) + ; + + session->closeAsyncThread(); + + EXPECT_EQ(0u, session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->apiEvents.size()); +} + +struct DebugApiRegistersAccessFixture : public DebugApiLinuxFixture { + void SetUp() { + hwInfo = *NEO::defaultHwInfo.get(); + hwInfo.gtSystemInfo.SubSliceCount = 6 * hwInfo.gtSystemInfo.SliceCount; + + DebugApiLinuxFixture::SetUp(&hwInfo); + + mockBuiltins = new MockBuiltins(); + mockBuiltins->stateSaveAreaHeader = MockSipData::createStateSaveAreaHeader(1); + neoDevice->executionEnvironment->rootDeviceEnvironments[0]->builtins.reset(mockBuiltins); + session = std::make_unique(zet_debug_config_t{}, device, 0); + session->clientHandle = MockDebugSessionLinux::mockClientHandle; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->contextsCreated[ctxHandle].vm = vmHandle; + auto &hwHelper = HwHelper::get(hwInfo.platform.eRenderCoreFamily); + maxDbgSurfaceSize = hwHelper.getSipKernelMaxDbgSurfaceSize(hwInfo); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle] = {stateSaveAreaGpuVa, maxDbgSurfaceSize}; + session->allThreadsStopped = true; + } + + void TearDown() { + DebugApiLinuxFixture::TearDown(); + } + NEO::HardwareInfo hwInfo; + std::unique_ptr session; + MockBuiltins *mockBuiltins = nullptr; + MockIoctlHandler *ioctlHandler = nullptr; + uint64_t ctxHandle = 2; + uint64_t vmHandle = 7; + uint64_t stateSaveAreaGpuVa = 0x123400000000; + size_t maxDbgSurfaceSize = 0; +}; + +using DebugApiRegistersAccessTest = Test; + +TEST_F(DebugApiRegistersAccessTest, givenInvalidClientHandleWhenReadRegistersCalledThenErrorIsReturned) { + session->clientHandle = MockDebugSessionLinux::invalidClientHandle; + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenCorruptedTssAreaWhenReadRegistersCalledThenErrorUnknownIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + + ioctlHandler->setPreadMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + ioctlHandler->setPwriteMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + + session->ioctlHandler.reset(ioctlHandler); + session->vmHandle = 7; + memcpy(session->stateSaveAreaHeader.data(), "garbage", 8); + session->ensureThreadStopped({0, 0, 0, 0}); + + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenInvalidRegistersIndicesWhenReadRegistersCalledThenErrorInvalidArgumentIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 128, 1, nullptr)); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 127, 2, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenStateSaveAreaNotCapturedWhenReadRegistersIsCalledThenErrorUnknownIsReturned) { + auto bindInfoSave = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle]; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo.clear(); + + char r0[32]; + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, r0)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle] = bindInfoSave; +} + +TEST_F(DebugApiRegistersAccessTest, givenReadGpuMemoryFailedWhenReadRegistersCalledThenErrorUnknownIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + auto ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapFail = true; + session->ioctlHandler.reset(ioctlHandler); + + char r0[32]; + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, r0)); +} + +TEST_F(DebugApiRegistersAccessTest, givenCorruptedSrMagicWhenReadRegistersCalledThenErrorUnknownIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + session->ioctlHandler.reset(ioctlHandler); + + auto pStateSaveAreaHeader = reinterpret_cast(session->stateSaveAreaHeader.data()); + strcpy(session->stateSaveAreaHeader.data() + threadSlotOffset(pStateSaveAreaHeader, 0, 0, 0, 0) + pStateSaveAreaHeader->regHeader.sr_magic_offset, "garbage"); // NOLINT(clang-analyzer-security.insecureAPI.strcpy) + + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenReadRegistersCalledCorrectValuesRead) { + SIP::version version = {1, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + + ioctlHandler->setPreadMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + ioctlHandler->setPwriteMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + + session->ioctlHandler.reset(ioctlHandler); + session->vmHandle = 7; + + char grf[32] = {0}; + char grfRef[32] = {0}; + + session->ensureThreadStopped({0, 0, 0, 0}); + for (uint32_t reg = 0; reg < 20; ++reg) { + memset(grfRef, 'a' + reg, 32); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, reg, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + } + + session->ensureThreadStopped({0, 3, 7, 3}); + for (uint32_t reg = 0; reg < 20; ++reg) { + memset(grfRef, 'a' + reg, 32); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 3, 7, 3}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, reg, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + } + + session->ensureThreadStopped({0, 5, 15, 6}); + for (uint32_t reg = 0; reg < 20; ++reg) { + memset(grfRef, 'a' + reg, 32); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 5, 15, 6}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, reg, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + } +} + +TEST_F(DebugApiRegistersAccessTest, givenInvalidClientHandleWhenWriteRegistersCalledThenErrorIsReturned) { + session->clientHandle = MockDebugSessionLinux::invalidClientHandle; + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenCorruptedTssAreaWhenWriteRegistersCalledThenErrorUnknownIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + session->ioctlHandler.reset(ioctlHandler); + + memcpy(session->stateSaveAreaHeader.data(), "garbage", 8); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenInvalidRegistersIndicesWhenWriteRegistersCalledThenErrorInvalidArgumentIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 128, 1, nullptr)); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 127, 2, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenStateSaveAreaNotCapturedWhenWriteRegistersIsCalledThenErrorUnknownIsReturned) { + auto bindInfoSave = session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle]; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo.clear(); + + char r0[32]; + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, r0)); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle] = bindInfoSave; +} + +TEST_F(DebugApiRegistersAccessTest, givenWriteGpuMemoryFailedWhenWriteRegistersCalledThenErrorUnknownIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + session->ioctlHandler.reset(ioctlHandler); + ioctlHandler->mmapFail = true; + + char r0[32]; + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, r0)); +} + +TEST_F(DebugApiRegistersAccessTest, givenCorruptedSrMagicWhenWriteRegistersCalledThenErrorUnknownIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + session->ioctlHandler.reset(ioctlHandler); + + auto pStateSaveAreaHeader = reinterpret_cast(session->stateSaveAreaHeader.data()); + strcpy(session->stateSaveAreaHeader.data() + threadSlotOffset(pStateSaveAreaHeader, 0, 0, 0, 0) + pStateSaveAreaHeader->regHeader.sr_magic_offset, "garbage"); // NOLINT(clang-analyzer-security.insecureAPI.strcpy) + + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, givenWriteRegistersCalledCorrectValuesRead) { + SIP::version version = {1, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + + ioctlHandler->setPreadMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + ioctlHandler->setPwriteMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + + session->ioctlHandler.reset(ioctlHandler); + session->vmHandle = 7; + + char grf[32] = {0}; + char grfRef[32] = {0}; + + // grfs for 0/0/0/0 - very first eu thread + memset(grfRef, 'k', 32); // 'a' + 10, r10 + session->ensureThreadStopped({0, 0, 0, 0}); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + memset(grfRef, 'x', 32); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grfRef)); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + + // grfs for 0/3/7/3 - somewhere in the middle + memset(grfRef, 'k', 32); + session->ensureThreadStopped({0, 3, 7, 3}); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 3, 7, 3}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + memset(grfRef, 'y', 32); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugWriteRegisters(session->toHandle(), {0, 3, 7, 3}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grfRef)); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 3, 7, 3}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + + // grfs for 0/5/15/6 - very last eu thread + memset(grfRef, 'k', 32); + session->ensureThreadStopped({0, 5, 15, 6}); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 5, 15, 6}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); + memset(grfRef, 'z', 32); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugWriteRegisters(session->toHandle(), {0, 5, 15, 6}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grfRef)); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), {0, 5, 15, 6}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 10, 1, grf)); + EXPECT_EQ(0, memcmp(grf, grfRef, 32)); +} + +TEST_F(DebugApiRegistersAccessTest, givenNoneThreadsStoppedWhenWriteRegistersCalledThenErrorNotAvailableReturned) { + session->allThreadsStopped = false; + + char grf[32] = {0}; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, grf)); +} + +TEST_F(DebugApiRegistersAccessTest, GivenThreadWhenReadingSystemRoutineIdentThenCorrectStateSaveAreaLocationIsRead) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + + ioctlHandler->setPreadMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + ioctlHandler->setPwriteMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + + session->ioctlHandler.reset(ioctlHandler); + EuThread thread({0, 0, 0, 0, 0}); + + SIP::sr_ident srIdent = {}; + auto result = session->readSystemRoutineIdent(&thread, vmHandle, srIdent); + + EXPECT_TRUE(result); + + EXPECT_EQ(2u, srIdent.count); + EXPECT_EQ(2u, srIdent.version.major); + EXPECT_EQ(0u, srIdent.version.minor); + EXPECT_EQ(0u, srIdent.version.patch); + EXPECT_STREQ("srmagic", srIdent.magic); + + EuThread thread2({0, 0, 0, 7, 3}); + + result = session->readSystemRoutineIdent(&thread, vmHandle, srIdent); + + EXPECT_TRUE(result); + + EXPECT_EQ(2u, srIdent.count); + EXPECT_EQ(2u, srIdent.version.major); + EXPECT_EQ(0u, srIdent.version.minor); + EXPECT_EQ(0u, srIdent.version.patch); + EXPECT_STREQ("srmagic", srIdent.magic); +} + +TEST_F(DebugApiRegistersAccessTest, GivenNoVmHandleWhenReadingSystemRoutineIdentThenFalseIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + ioctlHandler = new MockIoctlHandler; + + session->ioctlHandler.reset(ioctlHandler); + + EuThread thread({0, 0, 0, 0, 0}); + + SIP::sr_ident srIdent = {}; + auto result = session->readSystemRoutineIdent(&thread, DebugSessionLinux::invalidHandle, srIdent); + + EXPECT_FALSE(result); +} + +TEST_F(DebugApiRegistersAccessTest, GivenNoStatSaveAreaWhenReadingSystemRoutineIdentThenErrorReturned) { + ioctlHandler = new MockIoctlHandler; + session->ioctlHandler.reset(ioctlHandler); + EuThread thread({0, 0, 0, 0, 0}); + + SIP::sr_ident srIdent = {}; + auto result = session->readSystemRoutineIdent(&thread, 0x1234u, srIdent); + + EXPECT_FALSE(result); +} + +TEST_F(DebugApiRegistersAccessTest, GivenMemReadFailureWhenReadingSystemRoutineIdentThenFalseIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + session->ioctlHandler.reset(ioctlHandler); + ioctlHandler->mmapFail = true; + ioctlHandler->preadRetVal = -1; + + EuThread thread({0, 0, 0, 0, 0}); + + SIP::sr_ident srIdent = {}; + auto result = session->readSystemRoutineIdent(&thread, vmHandle, srIdent); + + EXPECT_FALSE(result); +} + +TEST_F(DebugApiRegistersAccessTest, GivenCSSANotBoundWhenReadingSystemRoutineIdentThenFalseIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + session->ioctlHandler.reset(ioctlHandler); + + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo.clear(); + + EuThread thread({0, 0, 0, 0, 0}); + + SIP::sr_ident srIdent = {}; + auto result = session->readSystemRoutineIdent(&thread, 0x1234u, srIdent); + + EXPECT_FALSE(result); +} + +TEST_F(DebugApiRegistersAccessTest, GivenThreadWithInvalidVmIdWhenReadSbaBufferCalledThenErrorNotAvailableIsReturned) { + session->vmHandle = L0::DebugSessionLinux::invalidHandle; + ze_device_thread_t thread{0, 0, 0, 0}; + session->ensureThreadStopped(thread); + SbaTrackedAddresses sba; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->readSbaBuffer(session->convertToThreadId(thread), sba)); +} + +TEST_F(DebugApiRegistersAccessTest, GivenVmHandleNotFoundWhenReadSbaBufferCalledThenErrorUnknownIsReturned) { + session->vmHandle = 7; + ze_device_thread_t thread{0, 0, 0, 0}; + session->ensureThreadStopped(thread); + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo.clear(); + SbaTrackedAddresses sba; + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, session->readSbaBuffer(session->convertToThreadId(thread), sba)); +} + +struct MockRenderSurfaceState { + uint32_t unused0[8]; + uint64_t SurfaceBaseAddress; + uint32_t unused1[6]; +}; +static_assert(64 == sizeof(MockRenderSurfaceState)); + +void sbaInit(std::vector &stateSaveArea, uint64_t stateSaveAreaGpuVa, SbaTrackedAddresses &sba, uint32_t r0[8]) { + auto &hwInfo = *NEO::defaultHwInfo.get(); + auto &hwHelper = HwHelper::get(hwInfo.platform.eRenderCoreFamily); + auto maxDbgSurfaceSize = hwHelper.getSipKernelMaxDbgSurfaceSize(hwInfo); + uint64_t surfaceStateBaseAddress = stateSaveAreaGpuVa + maxDbgSurfaceSize + sizeof(SbaTrackedAddresses); + uint32_t renderSurfaceStateOffset = 256; + r0[4] = 0xAAAAAAAA; + r0[5] = ((renderSurfaceStateOffset) >> 6) << 10; + + sba.GeneralStateBaseAddress = 0x11111111; + sba.SurfaceStateBaseAddress = surfaceStateBaseAddress; + sba.DynamicStateBaseAddress = 0x33333333; + sba.IndirectObjectBaseAddress = 0x44444444; + sba.InstructionBaseAddress = 0x55555555; + sba.BindlessSurfaceStateBaseAddress = 0x66666666; + sba.BindlessSamplerStateBaseAddress = 0x77777777; + + char *sbaCpuPtr = stateSaveArea.data() + maxDbgSurfaceSize; + char *rssCpuPtr = sbaCpuPtr + sizeof(SbaTrackedAddresses) + renderSurfaceStateOffset; + memcpy(sbaCpuPtr, &sba, sizeof(sba)); + reinterpret_cast(rssCpuPtr)->SurfaceBaseAddress = 0xBA5EBA5E; +} + +TEST_F(DebugApiRegistersAccessTest, GivenReadSbaBufferCalledThenSbaBufferIsRead) { + + SIP::version version = {1, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + + ioctlHandler->setPreadMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + ioctlHandler->setPwriteMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + + session->ioctlHandler.reset(ioctlHandler); + session->vmHandle = 7; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo[vmHandle] = {stateSaveAreaGpuVa + maxDbgSurfaceSize, sizeof(SbaTrackedAddresses)}; + ze_device_thread_t thread{0, 0, 0, 0}; + session->ensureThreadStopped(thread); + + SbaTrackedAddresses sba, sbaExpected; + uint32_t r0[8]; + sbaInit(session->stateSaveAreaHeader, stateSaveAreaGpuVa, sbaExpected, r0); + + EXPECT_EQ(ZE_RESULT_SUCCESS, session->readSbaBuffer(session->convertToThreadId(thread), sba)); + EXPECT_EQ(sbaExpected.GeneralStateBaseAddress, sba.GeneralStateBaseAddress); + EXPECT_EQ(sbaExpected.SurfaceStateBaseAddress, sba.SurfaceStateBaseAddress); + EXPECT_EQ(sbaExpected.DynamicStateBaseAddress, sba.DynamicStateBaseAddress); + EXPECT_EQ(sbaExpected.IndirectObjectBaseAddress, sba.IndirectObjectBaseAddress); + EXPECT_EQ(sbaExpected.InstructionBaseAddress, sba.InstructionBaseAddress); + EXPECT_EQ(sbaExpected.BindlessSurfaceStateBaseAddress, sba.BindlessSurfaceStateBaseAddress); + EXPECT_EQ(sbaExpected.BindlessSamplerStateBaseAddress, sba.BindlessSamplerStateBaseAddress); +} + +TEST_F(DebugApiRegistersAccessTest, givenInvalidSbaRegistersIndicesWhenReadSbaRegistersCalledThenErrorInvalidArgumentIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU, 9, 1, nullptr)); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, zetDebugReadRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU, 8, 2, nullptr)); +} + +TEST_F(DebugApiRegistersAccessTest, GivenReadSbaRegistersCalledThenSbaRegistersAreRead) { + SIP::version version = {1, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + + ioctlHandler->setPreadMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + ioctlHandler->setPwriteMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + + session->ioctlHandler.reset(ioctlHandler); + session->vmHandle = 7; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo[vmHandle] = {stateSaveAreaGpuVa + maxDbgSurfaceSize, sizeof(SbaTrackedAddresses)}; + ze_device_thread_t thread{0, 0, 0, 0}; + session->ensureThreadStopped(thread); + + SbaTrackedAddresses sbaExpected; + uint32_t r0[8]; + sbaInit(session->stateSaveAreaHeader, stateSaveAreaGpuVa, sbaExpected, r0); + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugWriteRegisters(session->toHandle(), thread, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, r0)); + + uint64_t sba[9]; + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), thread, ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU, 0, 9, sba)); + + EXPECT_EQ(sbaExpected.GeneralStateBaseAddress, sba[0]); + EXPECT_EQ(sbaExpected.SurfaceStateBaseAddress, sba[1]); + EXPECT_EQ(sbaExpected.DynamicStateBaseAddress, sba[2]); + EXPECT_EQ(sbaExpected.IndirectObjectBaseAddress, sba[3]); + EXPECT_EQ(sbaExpected.InstructionBaseAddress, sba[4]); + EXPECT_EQ(sbaExpected.BindlessSurfaceStateBaseAddress, sba[5]); + EXPECT_EQ(sbaExpected.BindlessSamplerStateBaseAddress, sba[6]); + + uint64_t ExpectedBindingTableBaseAddress = ((r0[4] >> 5) << 5) + sbaExpected.SurfaceStateBaseAddress; + uint64_t ExpectedScratchSpaceBaseAddress = 0; + + auto &hwHelper = HwHelper::get(neoDevice->getHardwareInfo().platform.eRenderCoreFamily); + if (hwHelper.isScratchSpaceSurfaceStateAccessible()) { + ExpectedScratchSpaceBaseAddress = 0xBA5EBA5E; + } else { + ExpectedScratchSpaceBaseAddress = ((r0[5] >> 10) << 10) + sbaExpected.GeneralStateBaseAddress; + } + + EXPECT_EQ(ExpectedBindingTableBaseAddress, sba[7]); + EXPECT_EQ(ExpectedScratchSpaceBaseAddress, sba[8]); +} + +TEST_F(DebugApiRegistersAccessTest, GivenScarcthPointerAndZeroAddressInSurfaceStateWhenGettingScratchBaseRegThenValueIsZero) { + SIP::version version = {1, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + + ioctlHandler = new MockIoctlHandler; + ioctlHandler->mmapRet = session->stateSaveAreaHeader.data(); + ioctlHandler->mmapBase = stateSaveAreaGpuVa; + + ioctlHandler->setPreadMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + ioctlHandler->setPwriteMemory(session->stateSaveAreaHeader.data(), session->stateSaveAreaHeader.size(), stateSaveAreaGpuVa); + + session->ioctlHandler.reset(ioctlHandler); + session->vmHandle = 7; + session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToStateBaseAreaBindInfo[vmHandle] = {stateSaveAreaGpuVa + maxDbgSurfaceSize, sizeof(SbaTrackedAddresses)}; + ze_device_thread_t thread{0, 3, 7, 3}; + session->ensureThreadStopped(thread); + + SbaTrackedAddresses sbaExpected{}; + uint32_t r0[8]; + + uint64_t surfaceStateBaseAddress = stateSaveAreaGpuVa + maxDbgSurfaceSize + sizeof(SbaTrackedAddresses); + uint32_t renderSurfaceStateOffset = 256; + r0[4] = 0xAAAAAAAA; + r0[5] = ((renderSurfaceStateOffset) >> 6) << 10; + + auto &hwHelper = HwHelper::get(neoDevice->getHardwareInfo().platform.eRenderCoreFamily); + if (!hwHelper.isScratchSpaceSurfaceStateAccessible()) { + r0[5] = 0; + } + sbaExpected.SurfaceStateBaseAddress = surfaceStateBaseAddress; + + char *sbaCpuPtr = session->stateSaveAreaHeader.data() + maxDbgSurfaceSize; + char *rssCpuPtr = sbaCpuPtr + sizeof(SbaTrackedAddresses) + renderSurfaceStateOffset; + memcpy(sbaCpuPtr, &sbaExpected, sizeof(sbaExpected)); + reinterpret_cast(rssCpuPtr)->SurfaceBaseAddress = 0; + + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugWriteRegisters(session->toHandle(), thread, ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU, 0, 1, r0)); + + uint64_t sba[9]; + EXPECT_EQ(ZE_RESULT_SUCCESS, zetDebugReadRegisters(session->toHandle(), thread, ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU, 0, 9, sba)); + + EXPECT_EQ(sbaExpected.SurfaceStateBaseAddress, sba[1]); + + const uint64_t ExpectedScratchSpaceBaseAddress = 0; + EXPECT_EQ(ExpectedScratchSpaceBaseAddress, sba[8]); +} + +TEST_F(DebugApiRegistersAccessTest, givenWriteSbaRegistersCalledThenErrorInvalidArgumentIsReturned) { + SIP::version version = {2, 0, 0}; + initStateSaveArea(session->stateSaveAreaHeader, version); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, zetDebugWriteRegisters(session->toHandle(), {0, 0, 0, 0}, ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU, 0, 1, nullptr)); +} + +} // namespace ult +} // namespace L0 diff --git a/opencl/test/unit_test/os_interface/linux/drm_memory_manager_tests.cpp b/opencl/test/unit_test/os_interface/linux/drm_memory_manager_tests.cpp index 238a4c0fc6..f935a8e4ed 100644 --- a/opencl/test/unit_test/os_interface/linux/drm_memory_manager_tests.cpp +++ b/opencl/test/unit_test/os_interface/linux/drm_memory_manager_tests.cpp @@ -4185,7 +4185,7 @@ TEST(DrmMemoryMangerTest, givenMultipleRootDeviceWhenMemoryManagerGetsDrmThenDrm TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeShouldBeRegisteredThenBoHasBindExtHandleAdded) { DrmMockResources drm(*executionEnvironment->rootDeviceEnvironments[0]); - for (uint32_t i = 3; i < 3 + static_cast(Drm::ResourceClass::MaxSize); i++) { + for (uint32_t i = 3; i < 3 + static_cast(DrmResourceClass::MaxSize); i++) { drm.classHandles.push_back(i); } @@ -4195,9 +4195,9 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeSho allocation.bufferObjects[0] = &bo; allocation.registerBOBindExtHandle(&drm); EXPECT_EQ(DrmMockResources::registerResourceReturnHandle, bo.bindExtHandles[0]); - EXPECT_EQ(Drm::ResourceClass::ContextSaveArea, drm.registeredClass); + EXPECT_EQ(DrmResourceClass::ContextSaveArea, drm.registeredClass); } - drm.registeredClass = Drm::ResourceClass::MaxSize; + drm.registeredClass = DrmResourceClass::MaxSize; { MockBufferObject bo(&drm, 3, 0, 0, 1); @@ -4205,9 +4205,9 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeSho allocation.bufferObjects[0] = &bo; allocation.registerBOBindExtHandle(&drm); EXPECT_EQ(DrmMockResources::registerResourceReturnHandle, bo.bindExtHandles[0]); - EXPECT_EQ(Drm::ResourceClass::SbaTrackingBuffer, drm.registeredClass); + EXPECT_EQ(DrmResourceClass::SbaTrackingBuffer, drm.registeredClass); } - drm.registeredClass = Drm::ResourceClass::MaxSize; + drm.registeredClass = DrmResourceClass::MaxSize; { MockBufferObject bo(&drm, 3, 0, 0, 1); @@ -4215,9 +4215,9 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeSho allocation.bufferObjects[0] = &bo; allocation.registerBOBindExtHandle(&drm); EXPECT_EQ(DrmMockResources::registerResourceReturnHandle, bo.bindExtHandles[0]); - EXPECT_EQ(Drm::ResourceClass::Isa, drm.registeredClass); + EXPECT_EQ(DrmResourceClass::Isa, drm.registeredClass); } - drm.registeredClass = Drm::ResourceClass::MaxSize; + drm.registeredClass = DrmResourceClass::MaxSize; { MockBufferObject bo(&drm, 3, 0, 0, 1); @@ -4225,10 +4225,10 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeSho allocation.bufferObjects[0] = &bo; allocation.registerBOBindExtHandle(&drm); EXPECT_EQ(DrmMockResources::registerResourceReturnHandle, bo.bindExtHandles[0]); - EXPECT_EQ(Drm::ResourceClass::ModuleHeapDebugArea, drm.registeredClass); + EXPECT_EQ(DrmResourceClass::ModuleHeapDebugArea, drm.registeredClass); } - drm.registeredClass = Drm::ResourceClass::MaxSize; + drm.registeredClass = DrmResourceClass::MaxSize; { MockBufferObject bo(&drm, 3, 0, 0, 1); @@ -4236,16 +4236,16 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeSho allocation.bufferObjects[0] = &bo; allocation.registerBOBindExtHandle(&drm); EXPECT_EQ(0u, bo.bindExtHandles.size()); - EXPECT_EQ(Drm::ResourceClass::MaxSize, drm.registeredClass); + EXPECT_EQ(DrmResourceClass::MaxSize, drm.registeredClass); } } TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeShouldNotBeRegisteredThenNoBindHandleCreated) { DrmMockResources drm(*executionEnvironment->rootDeviceEnvironments[0]); - drm.registeredClass = Drm::ResourceClass::MaxSize; + drm.registeredClass = DrmResourceClass::MaxSize; - for (uint32_t i = 3; i < 3 + static_cast(Drm::ResourceClass::MaxSize); i++) { + for (uint32_t i = 3; i < 3 + static_cast(DrmResourceClass::MaxSize); i++) { drm.classHandles.push_back(i); } @@ -4256,7 +4256,7 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationTypeSho allocation.registerBOBindExtHandle(&drm); EXPECT_EQ(0u, bo.bindExtHandles.size()); } - EXPECT_EQ(Drm::ResourceClass::MaxSize, drm.registeredClass); + EXPECT_EQ(DrmResourceClass::MaxSize, drm.registeredClass); } TEST_F(DrmAllocationTests, givenResourceRegistrationNotEnabledWhenRegisteringBindExtHandleThenHandleIsNotAddedToBo) { @@ -4268,7 +4268,7 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationNotEnabledWhenRegisteringBin allocation.bufferObjects[0] = &bo; allocation.registerBOBindExtHandle(&drm); EXPECT_EQ(0u, bo.bindExtHandles.size()); - EXPECT_EQ(Drm::ResourceClass::MaxSize, drm.registeredClass); + EXPECT_EQ(DrmResourceClass::MaxSize, drm.registeredClass); } TEST(DrmMemoryManager, givenTrackedAllocationTypeAndDisabledRegistrationInDrmWhenAllocatingThenRegisterBoBindExtHandleIsNotCalled) { @@ -4289,7 +4289,7 @@ TEST(DrmMemoryManager, givenTrackedAllocationTypeAndDisabledRegistrationInDrmWhe memoryManager->registerAllocationInOs(&allocation); EXPECT_FALSE(allocation.registerBOBindExtHandleCalled); - EXPECT_EQ(Drm::ResourceClass::MaxSize, mockDrm->registeredClass); + EXPECT_EQ(DrmResourceClass::MaxSize, mockDrm->registeredClass); } TEST(DrmMemoryManager, givenResourceRegistrationEnabledAndAllocTypeToCaptureWhenRegisteringAllocationInOsThenItIsMarkedForCapture) { @@ -4328,7 +4328,7 @@ TEST(DrmMemoryManager, givenTrackedAllocationTypeWhenAllocatingThenAllocationIsR executionEnvironment->rootDeviceEnvironments[0]->initGmm(); auto memoryManager = std::make_unique(false, false, false, *executionEnvironment); - for (uint32_t i = 3; i < 3 + static_cast(Drm::ResourceClass::MaxSize); i++) { + for (uint32_t i = 3; i < 3 + static_cast(DrmResourceClass::MaxSize); i++) { mockDrm->classHandles.push_back(i); } @@ -4340,7 +4340,7 @@ TEST(DrmMemoryManager, givenTrackedAllocationTypeWhenAllocatingThenAllocationIsR properties.gpuAddress = 0x20000; auto sbaAllocation = memoryManager->allocateGraphicsMemoryWithProperties(properties); - EXPECT_EQ(Drm::ResourceClass::SbaTrackingBuffer, mockDrm->registeredClass); + EXPECT_EQ(DrmResourceClass::SbaTrackingBuffer, mockDrm->registeredClass); EXPECT_EQ(sizeof(uint64_t), mockDrm->registeredDataSize); uint64_t *data = reinterpret_cast(mockDrm->registeredData); @@ -4359,7 +4359,7 @@ TEST(DrmMemoryManager, givenTrackedAllocationTypeWhenFreeingThenRegisteredHandle executionEnvironment->rootDeviceEnvironments[0]->initGmm(); auto memoryManager = std::make_unique(false, false, false, *executionEnvironment); - for (uint32_t i = 3; i < 3 + static_cast(Drm::ResourceClass::MaxSize); i++) { + for (uint32_t i = 3; i < 3 + static_cast(DrmResourceClass::MaxSize); i++) { mockDrm->classHandles.push_back(i); } @@ -4386,7 +4386,7 @@ TEST(DrmMemoryManager, givenNullBoWhenRegisteringBindExtHandleThenEarlyReturn) { executionEnvironment->rootDeviceEnvironments[0]->setHwInfo(defaultHwInfo.get()); auto mockDrm = std::make_unique(*executionEnvironment->rootDeviceEnvironments[0]); - for (uint32_t i = 3; i < 3 + static_cast(Drm::ResourceClass::MaxSize); i++) { + for (uint32_t i = 3; i < 3 + static_cast(DrmResourceClass::MaxSize); i++) { mockDrm->classHandles.push_back(i); } @@ -4416,11 +4416,11 @@ TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenAllocationIsRegis TEST_F(DrmAllocationTests, givenResourceRegistrationEnabledWhenIsaIsRegisteredThenCookieIsAddedToBoHandle) { DrmMockResources drm(*executionEnvironment->rootDeviceEnvironments[0]); - for (uint32_t i = 3; i < 3 + static_cast(Drm::ResourceClass::MaxSize); i++) { + for (uint32_t i = 3; i < 3 + static_cast(DrmResourceClass::MaxSize); i++) { drm.classHandles.push_back(i); } - drm.registeredClass = Drm::ResourceClass::MaxSize; + drm.registeredClass = DrmResourceClass::MaxSize; MockBufferObject bo(&drm, 3, 0, 0, 1); MockDrmAllocation allocation(AllocationType::KERNEL_ISA, MemoryPool::System4KBPages); diff --git a/opencl/test/unit_test/os_interface/linux/drm_uuid_tests.cpp b/opencl/test/unit_test/os_interface/linux/drm_uuid_tests.cpp index d44c93d7b0..af88405d08 100644 --- a/opencl/test/unit_test/os_interface/linux/drm_uuid_tests.cpp +++ b/opencl/test/unit_test/os_interface/linux/drm_uuid_tests.cpp @@ -34,7 +34,7 @@ TEST(DrmUuidTest, GivenDrmWhenGeneratingElfUUIDThenCorrectStringsAreReturned) { executionEnvironment->prepareRootDeviceEnvironments(1); DrmMock drm{*executionEnvironment->rootDeviceEnvironments[0]}; - std::string elfClassUuid = classNamesToUuid[static_cast(Drm::ResourceClass::Elf)].second; + std::string elfClassUuid = classNamesToUuid[static_cast(DrmResourceClass::Elf)].second; std::string Uuid1stElfClass = elfClassUuid.substr(0, 18); char data[] = "abc"; @@ -53,25 +53,25 @@ TEST(DrmUuidTest, GivenDrmWhenGeneratingElfUUIDThenCorrectStringsAreReturned) { } TEST(DrmUuidTest, whenResourceClassIsUsedToIndexClassNamesThenCorrectNamesAreReturned) { - EXPECT_STREQ(classNamesToUuid[static_cast(Drm::ResourceClass::Elf)].first, "I915_UUID_CLASS_ELF_BINARY"); - EXPECT_STREQ(classNamesToUuid[static_cast(Drm::ResourceClass::Isa)].first, "I915_UUID_CLASS_ISA_BYTECODE"); - EXPECT_STREQ(classNamesToUuid[static_cast(Drm::ResourceClass::ContextSaveArea)].first, "I915_UUID_L0_SIP_AREA"); - EXPECT_STREQ(classNamesToUuid[static_cast(Drm::ResourceClass::ModuleHeapDebugArea)].first, "I915_UUID_L0_MODULE_AREA"); - EXPECT_STREQ(classNamesToUuid[static_cast(Drm::ResourceClass::SbaTrackingBuffer)].first, "I915_UUID_L0_SBA_AREA"); - EXPECT_STREQ(classNamesToUuid[static_cast(Drm::ResourceClass::L0ZebinModule)].first, "L0_ZEBIN_MODULE"); + EXPECT_STREQ(classNamesToUuid[static_cast(DrmResourceClass::Elf)].first, "I915_UUID_CLASS_ELF_BINARY"); + EXPECT_STREQ(classNamesToUuid[static_cast(DrmResourceClass::Isa)].first, "I915_UUID_CLASS_ISA_BYTECODE"); + EXPECT_STREQ(classNamesToUuid[static_cast(DrmResourceClass::ContextSaveArea)].first, "I915_UUID_L0_SIP_AREA"); + EXPECT_STREQ(classNamesToUuid[static_cast(DrmResourceClass::ModuleHeapDebugArea)].first, "I915_UUID_L0_MODULE_AREA"); + EXPECT_STREQ(classNamesToUuid[static_cast(DrmResourceClass::SbaTrackingBuffer)].first, "I915_UUID_L0_SBA_AREA"); + EXPECT_STREQ(classNamesToUuid[static_cast(DrmResourceClass::L0ZebinModule)].first, "L0_ZEBIN_MODULE"); } TEST(DrmUuidTest, givenUuidStringWhenGettingClassIndexThenCorrectIndexForValidStringsIsReturned) { uint32_t index = 100; - auto validUuid = DrmUuid::getClassUuidIndex(classNamesToUuid[static_cast(Drm::ResourceClass::ContextSaveArea)].second, index); + auto validUuid = DrmUuid::getClassUuidIndex(classNamesToUuid[static_cast(DrmResourceClass::ContextSaveArea)].second, index); EXPECT_TRUE(validUuid); - EXPECT_EQ(static_cast(Drm::ResourceClass::ContextSaveArea), index); + EXPECT_EQ(static_cast(DrmResourceClass::ContextSaveArea), index); - validUuid = DrmUuid::getClassUuidIndex(classNamesToUuid[static_cast(Drm::ResourceClass::ModuleHeapDebugArea)].second, index); + validUuid = DrmUuid::getClassUuidIndex(classNamesToUuid[static_cast(DrmResourceClass::ModuleHeapDebugArea)].second, index); EXPECT_TRUE(validUuid); - EXPECT_EQ(static_cast(Drm::ResourceClass::ModuleHeapDebugArea), index); + EXPECT_EQ(static_cast(DrmResourceClass::ModuleHeapDebugArea), index); index = 100; validUuid = DrmUuid::getClassUuidIndex("invalid", index); diff --git a/opencl/test/unit_test/test_files/igdrcl.config b/opencl/test/unit_test/test_files/igdrcl.config index 1efe9272b9..582dba6d3e 100644 --- a/opencl/test/unit_test/test_files/igdrcl.config +++ b/opencl/test/unit_test/test_files/igdrcl.config @@ -419,4 +419,5 @@ UseContextEndOffsetForEventCompletion = -1 DirectSubmissionInsertExtraMiMemFenceCommands = -1 DirectSubmissionInsertSfenceInstructionPriorToSubmission = -1 EnableTimestampWaitForEvents = -1 -ForceWddmLowPriorityContextValue = -1 \ No newline at end of file +ForceWddmLowPriorityContextValue = -1 +EnableDebuggerMmapMemoryAccess = 0 diff --git a/shared/source/debug_settings/debug_variables_base.inl b/shared/source/debug_settings/debug_variables_base.inl index ccc175f66d..cc80f6769c 100644 --- a/shared/source/debug_settings/debug_variables_base.inl +++ b/shared/source/debug_settings/debug_variables_base.inl @@ -437,6 +437,7 @@ DECLARE_DEBUG_VARIABLE(bool, ForceDefaultThreadArbitrationPolicyIfNotSpecified, DECLARE_DEBUG_VARIABLE(int32_t, ProgramExtendedPipeControlPriorToNonPipelinedStateCommand, -1, "-1: default, 0: disable, 1: enable, Program additional extended version of PIPE CONTROL command before non pipelined state command") DECLARE_DEBUG_VARIABLE(int32_t, OverrideDrmRegion, -1, "-1: disable, 0+: override to given memory region for all allocations") DECLARE_DEBUG_VARIABLE(bool, ForceAllResourcesUncached, false, "When set, all memory operations for all resources are forced to UC. This overrides all caching-related debug variables and globally disables all caches") +DECLARE_DEBUG_VARIABLE(bool, EnableDebuggerMmapMemoryAccess, false, "Mmap used to access memory by debug api, valid only on Linux OS") /* Binary Cache */ DECLARE_DEBUG_VARIABLE(bool, BinaryCacheTrace, false, "enable cl_cache to produce .trace files with information about hash computation") diff --git a/shared/source/os_interface/linux/drm_allocation.cpp b/shared/source/os_interface/linux/drm_allocation.cpp index aa90de7c68..d1dc98c53a 100644 --- a/shared/source/os_interface/linux/drm_allocation.cpp +++ b/shared/source/os_interface/linux/drm_allocation.cpp @@ -175,26 +175,26 @@ void DrmAllocation::registerBOBindExtHandle(Drm *drm) { return; } - Drm::ResourceClass resourceClass = Drm::ResourceClass::MaxSize; + DrmResourceClass resourceClass = DrmResourceClass::MaxSize; switch (this->allocationType) { case AllocationType::DEBUG_CONTEXT_SAVE_AREA: - resourceClass = Drm::ResourceClass::ContextSaveArea; + resourceClass = DrmResourceClass::ContextSaveArea; break; case AllocationType::DEBUG_SBA_TRACKING_BUFFER: - resourceClass = Drm::ResourceClass::SbaTrackingBuffer; + resourceClass = DrmResourceClass::SbaTrackingBuffer; break; case AllocationType::KERNEL_ISA: - resourceClass = Drm::ResourceClass::Isa; + resourceClass = DrmResourceClass::Isa; break; case AllocationType::DEBUG_MODULE_AREA: - resourceClass = Drm::ResourceClass::ModuleHeapDebugArea; + resourceClass = DrmResourceClass::ModuleHeapDebugArea; break; default: break; } - if (resourceClass != Drm::ResourceClass::MaxSize) { + if (resourceClass != DrmResourceClass::MaxSize) { uint64_t gpuAddress = getGpuAddress(); auto handle = drm->registerResource(resourceClass, &gpuAddress, sizeof(gpuAddress)); registeredBoBindHandles.push_back(handle); @@ -205,7 +205,7 @@ void DrmAllocation::registerBOBindExtHandle(Drm *drm) { if (bo) { bo->addBindExtHandle(handle); bo->markForCapture(); - if (resourceClass == Drm::ResourceClass::Isa) { + if (resourceClass == DrmResourceClass::Isa) { auto cookieHandle = drm->registerIsaCookie(handle); bo->addBindExtHandle(cookieHandle); registeredBoBindHandles.push_back(cookieHandle); diff --git a/shared/source/os_interface/linux/drm_debug.cpp b/shared/source/os_interface/linux/drm_debug.cpp index ca69f3e236..ca4b523ed8 100644 --- a/shared/source/os_interface/linux/drm_debug.cpp +++ b/shared/source/os_interface/linux/drm_debug.cpp @@ -30,13 +30,13 @@ bool Drm::registerResourceClasses() { return true; } -uint32_t Drm::registerResource(ResourceClass classType, const void *data, size_t size) { +uint32_t Drm::registerResource(DrmResourceClass classType, const void *data, size_t size) { if (classHandles.size() < static_cast(classType)) { return 0; } std::string uuid; - if (classType == NEO::Drm::ResourceClass::Elf) { + if (classType == NEO::DrmResourceClass::Elf) { uuid = generateElfUUID(data); } else { uuid = generateUUID(); @@ -85,7 +85,7 @@ std::string Drm::generateUUID() { } std::string Drm::generateElfUUID(const void *data) { - std::string elf_class_uuid = classNamesToUuid[static_cast(Drm::ResourceClass::Elf)].second; + std::string elf_class_uuid = classNamesToUuid[static_cast(DrmResourceClass::Elf)].second; std::string UUID1st = elf_class_uuid.substr(0, 18); const char uuidString[] = "%s-%04" SCNx64 "-%012" SCNx64; diff --git a/shared/source/os_interface/linux/drm_debug.h b/shared/source/os_interface/linux/drm_debug.h index b34e460930..a1a46fcd8e 100644 --- a/shared/source/os_interface/linux/drm_debug.h +++ b/shared/source/os_interface/linux/drm_debug.h @@ -5,12 +5,22 @@ * */ -#include "shared/source/os_interface/linux/drm_neo.h" +#pragma once +#include #include #include namespace NEO { +enum class DrmResourceClass : uint32_t { + Elf, + Isa, + ModuleHeapDebugArea, + ContextSaveArea, + SbaTrackingBuffer, + L0ZebinModule, + MaxSize +}; /* @@ -44,7 +54,7 @@ UUID('285208b2-c5e0-5fcb-90bb-7576ed7a9697') */ -using ClassNamesArray = std::array, size_t(Drm::ResourceClass::MaxSize)>; +using ClassNamesArray = std::array, size_t(DrmResourceClass::MaxSize)>; const ClassNamesArray classNamesToUuid = {std::make_pair("I915_UUID_CLASS_ELF_BINARY", "31203221-8069-5a0a-9d43-94a4d3395ee1"), std::make_pair("I915_UUID_CLASS_ISA_BYTECODE", "53baed0a-12c3-5d19-aa69-ab9c51aa1039"), std::make_pair("I915_UUID_L0_MODULE_AREA", "a411e82e-16c9-58b7-bfb5-b209b8601d5f"), @@ -57,7 +67,7 @@ constexpr auto uuidL0CommandQueueHash = "285208b2-c5e0-5fcb-90bb-7576ed7a9697"; struct DrmUuid { static bool getClassUuidIndex(std::string uuid, uint32_t &index) { - for (uint32_t i = 0; i < uint32_t(Drm::ResourceClass::MaxSize); i++) { + for (uint32_t i = 0; i < uint32_t(DrmResourceClass::MaxSize); i++) { if (uuid == classNamesToUuid[i].second) { index = i; return true; diff --git a/shared/source/os_interface/linux/drm_neo.h b/shared/source/os_interface/linux/drm_neo.h index dc35503857..5534773df6 100644 --- a/shared/source/os_interface/linux/drm_neo.h +++ b/shared/source/os_interface/linux/drm_neo.h @@ -12,6 +12,7 @@ #include "shared/source/memory_manager/definitions/engine_limits.h" #include "shared/source/os_interface/driver_info.h" #include "shared/source/os_interface/linux/cache_info.h" +#include "shared/source/os_interface/linux/drm_debug.h" #include "shared/source/os_interface/linux/engine_info.h" #include "shared/source/os_interface/linux/hw_device_id.h" #include "shared/source/os_interface/linux/memory_info.h" @@ -66,16 +67,6 @@ class Drm : public DriverModel { static constexpr DriverModelType driverModelType = DriverModelType::DRM; static constexpr size_t completionFenceOffset = 1024; - enum class ResourceClass : uint32_t { - Elf, - Isa, - ModuleHeapDebugArea, - ContextSaveArea, - SbaTrackingBuffer, - L0ZebinModule, - MaxSize - }; - struct QueryTopologyData { int sliceCount; int subSliceCount; @@ -174,7 +165,7 @@ class Drm : public DriverModel { MOCKABLE_VIRTUAL void queryPageFaultSupport(); MOCKABLE_VIRTUAL bool hasPageFaultSupport() const; - MOCKABLE_VIRTUAL uint32_t registerResource(ResourceClass classType, const void *data, size_t size); + MOCKABLE_VIRTUAL uint32_t registerResource(DrmResourceClass classType, const void *data, size_t size); MOCKABLE_VIRTUAL void unregisterResource(uint32_t handle); MOCKABLE_VIRTUAL uint32_t registerIsaCookie(uint32_t isaHandle); @@ -318,7 +309,7 @@ class Drm : public DriverModel { std::mutex bindFenceMutex; std::array pagingFence; std::array fenceVal; - StackVec classHandles; + StackVec classHandles; std::vector virtualMemoryIds; std::unique_ptr hwDeviceId; diff --git a/shared/test/common/libult/linux/drm_mock.h b/shared/test/common/libult/linux/drm_mock.h index b3ec3725d2..cb4852d1fc 100644 --- a/shared/test/common/libult/linux/drm_mock.h +++ b/shared/test/common/libult/linux/drm_mock.h @@ -306,7 +306,7 @@ class DrmMockResources : public DrmMock { return true; } - uint32_t registerResource(ResourceClass classType, const void *data, size_t size) override { + uint32_t registerResource(DrmResourceClass classType, const void *data, size_t size) override { registeredClass = classType; memcpy_s(registeredData, sizeof(registeredData), data, size); registeredDataSize = size; @@ -339,7 +339,7 @@ class DrmMockResources : public DrmMock { uint32_t unregisteredHandle = 0; uint32_t unregisterCalledCount = 0; - ResourceClass registeredClass = ResourceClass::MaxSize; + DrmResourceClass registeredClass = DrmResourceClass::MaxSize; bool registerClassesCalled = false; uint64_t registeredData[128]; size_t registeredDataSize; diff --git a/shared/test/common/libult/linux/drm_mock_prelim_context.cpp b/shared/test/common/libult/linux/drm_mock_prelim_context.cpp index a3c54833d0..d8f0cafb09 100644 --- a/shared/test/common/libult/linux/drm_mock_prelim_context.cpp +++ b/shared/test/common/libult/linux/drm_mock_prelim_context.cpp @@ -241,6 +241,12 @@ int DrmMockPrelimContext::handlePrelimRequest(unsigned long request, void *arg) return uuidControlReturn; } break; + case PRELIM_DRM_IOCTL_I915_DEBUGGER_OPEN: { + auto debugger_open = reinterpret_cast(arg); + if (debugger_open->pid != 0 && debugger_open->events == 0) { + return debuggerOpenRetval; + } + } break; default: return -1; } diff --git a/shared/test/common/libult/linux/drm_mock_prelim_context.h b/shared/test/common/libult/linux/drm_mock_prelim_context.h index 4a775f216b..c138736107 100644 --- a/shared/test/common/libult/linux/drm_mock_prelim_context.h +++ b/shared/test/common/libult/linux/drm_mock_prelim_context.h @@ -135,6 +135,9 @@ struct DrmMockPrelimContext { bool failDistanceInfoQuery{false}; bool disableCcsSupport{false}; + // Debugger ioctls + int debuggerOpenRetval = 10; // debugFd + int handlePrelimRequest(unsigned long request, void *arg); bool handlePrelimQueryItem(void *arg); void storeVmBindExtensions(uint64_t ptr, bool bind); diff --git a/shared/test/common/os_interface/linux/sys_calls_linux_ult.cpp b/shared/test/common/os_interface/linux/sys_calls_linux_ult.cpp index 09ce41ece0..c6d04751b2 100644 --- a/shared/test/common/os_interface/linux/sys_calls_linux_ult.cpp +++ b/shared/test/common/os_interface/linux/sys_calls_linux_ult.cpp @@ -40,6 +40,7 @@ constexpr unsigned long int invalidIoctl = static_cast(-1); int setErrno = 0; int fstatFuncRetVal = 0; uint32_t preadFuncCalled = 0u; +uint32_t pwriteFuncCalled = 0u; uint32_t mmapFuncCalled = 0u; uint32_t munmapFuncCalled = 0u; bool isInvalidAILTest = false; @@ -184,6 +185,7 @@ ssize_t pread(int fd, void *buf, size_t count, off_t offset) { } ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) { + pwriteFuncCalled++; return 0; } diff --git a/shared/test/unit_test/os_interface/linux/drm_debug_prelim_tests.cpp b/shared/test/unit_test/os_interface/linux/drm_debug_prelim_tests.cpp index 1d6176a36f..3a423372da 100644 --- a/shared/test/unit_test/os_interface/linux/drm_debug_prelim_tests.cpp +++ b/shared/test/unit_test/os_interface/linux/drm_debug_prelim_tests.cpp @@ -66,7 +66,7 @@ TEST_F(DrmDebugPrelimTest, GivenUnsupportedUUIDRegisterIoctlWhenRegisteringClass TEST_F(DrmDebugPrelimTest, GivenNoClassesRegisteredWhenRegisteringResourceThenRegisterUUIDIoctlIsNotCalledAndZeroHandleReturned) { DrmQueryMock drm(*executionEnvironment->rootDeviceEnvironments[0]); - auto registeredHandle = drm.registerResource(Drm::ResourceClass::Isa, nullptr, 0); + auto registeredHandle = drm.registerResource(DrmResourceClass::Isa, nullptr, 0); EXPECT_EQ(0u, registeredHandle); EXPECT_EQ(0u, drm.ioctlCallsCount); } @@ -78,7 +78,7 @@ TEST_F(DrmDebugPrelimTest, GivenDrmWhenRegisteringResourceWithoutDataThenRegiste EXPECT_TRUE(result); const auto handle = drm.context.uuidHandle; - auto registeredHandle = drm.registerResource(Drm::ResourceClass::Isa, nullptr, 0); + auto registeredHandle = drm.registerResource(DrmResourceClass::Isa, nullptr, 0); EXPECT_EQ(handle + 1, drm.context.uuidHandle); EXPECT_EQ(handle, registeredHandle); @@ -89,7 +89,7 @@ TEST_F(DrmDebugPrelimTest, GivenDrmWhenRegisteringResourceWithoutDataThenRegiste EXPECT_EQ(nullptr, receivedUuid->ptr); EXPECT_EQ(0u, receivedUuid->size); EXPECT_TRUE(hasSubstr(std::string(receivedUuid->uuid), std::string("00000000-0000-0000"))); - EXPECT_EQ(drm.classHandles[static_cast(Drm::ResourceClass::Isa)], receivedUuid->uuidClass); + EXPECT_EQ(drm.classHandles[static_cast(DrmResourceClass::Isa)], receivedUuid->uuidClass); } TEST_F(DrmDebugPrelimTest, GivenDrmWhenRegisteringResourceWithDataThenRegisterUUIDIoctlIsCalledWithCorrectData) { @@ -101,7 +101,7 @@ TEST_F(DrmDebugPrelimTest, GivenDrmWhenRegisteringResourceWithDataThenRegisterUU auto handle = drm.context.uuidHandle; uint64_t data = 0x12345678; - auto registeredHandle = drm.registerResource(Drm::ResourceClass::Isa, &data, sizeof(uint64_t)); + auto registeredHandle = drm.registerResource(DrmResourceClass::Isa, &data, sizeof(uint64_t)); EXPECT_EQ(handle + 1, drm.context.uuidHandle); EXPECT_EQ(handle, registeredHandle); @@ -112,7 +112,7 @@ TEST_F(DrmDebugPrelimTest, GivenDrmWhenRegisteringResourceWithDataThenRegisterUU EXPECT_EQ(&data, receivedUuid->ptr); EXPECT_EQ(sizeof(uint64_t), receivedUuid->size); EXPECT_TRUE(hasSubstr(std::string(receivedUuid->uuid), std::string("00000000-0000-0000"))); - EXPECT_EQ(drm.classHandles[static_cast(Drm::ResourceClass::Isa)], receivedUuid->uuidClass); + EXPECT_EQ(drm.classHandles[static_cast(DrmResourceClass::Isa)], receivedUuid->uuidClass); EXPECT_EQ(0u, receivedUuid->flags); EXPECT_EQ(0u, receivedUuid->extensions); } @@ -124,7 +124,7 @@ TEST_F(DrmDebugPrelimTest, GivenDrmWhenUnregisteringResourceThenUnregisterUUIDIo EXPECT_TRUE(result); uint64_t data = 0x12345678; - auto registeredHandle = drm.registerResource(Drm::ResourceClass::Isa, &data, sizeof(uint64_t)); + auto registeredHandle = drm.registerResource(DrmResourceClass::Isa, &data, sizeof(uint64_t)); drm.unregisterResource(registeredHandle); @@ -184,7 +184,7 @@ TEST_F(DrmDebugPrelimTest, GivenDrmWhenRegisteringElfResourceWithoutDataThenRegi EXPECT_TRUE(result); auto handle = drm.context.uuidHandle; - auto registeredHandle = drm.registerResource(Drm::ResourceClass::Elf, nullptr, 0); + auto registeredHandle = drm.registerResource(DrmResourceClass::Elf, nullptr, 0); EXPECT_EQ(handle + 1, drm.context.uuidHandle); EXPECT_EQ(handle, registeredHandle); diff --git a/shared/test/unit_test/os_interface/linux/drm_debug_tests.cpp b/shared/test/unit_test/os_interface/linux/drm_debug_tests.cpp index 574aed3533..603e9f115b 100644 --- a/shared/test/unit_test/os_interface/linux/drm_debug_tests.cpp +++ b/shared/test/unit_test/os_interface/linux/drm_debug_tests.cpp @@ -37,7 +37,7 @@ TEST_F(DrmDebugTest, whenRegisterResourceClassesCalledThenTrueIsReturned) { TEST_F(DrmDebugTest, whenRegisterResourceCalledThenImplementationIsEmpty) { DrmMock drmMock(*executionEnvironment->rootDeviceEnvironments[0]); - auto handle = drmMock.registerResource(Drm::ResourceClass::MaxSize, nullptr, 0); + auto handle = drmMock.registerResource(DrmResourceClass::MaxSize, nullptr, 0); EXPECT_EQ(0u, handle); drmMock.unregisterResource(handle); EXPECT_EQ(0u, drmMock.ioctlCallsCount);