/* * Copyright (C) 2022-2023 Intel Corporation * * SPDX-License-Identifier: MIT * */ #include "level_zero/tools/source/debug/linux/prelim/debug_session.h" #include "shared/source/built_ins/sip.h" #include "shared/source/debug_settings/debug_settings_manager.h" #include "shared/source/gmm_helper/gmm_helper.h" #include "shared/source/helpers/aligned_memory.h" #include "shared/source/helpers/array_count.h" #include "shared/source/helpers/hw_info.h" #include "shared/source/helpers/sleep.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/gfx_core_helpers/l0_gfx_core_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, void *params); DebugSessionLinux::DebugSessionLinux(const zet_debug_config_t &config, Device *device, int debugFd, void *params) : DebugSessionImp(config, device), fd(debugFd) { ioctlHandler.reset(new IoctlHandler); if (params) { this->i915DebuggerVersion = reinterpret_cast(params)->version; } if (this->i915DebuggerVersion >= 3) { this->blockOnFenceMode = true; } for (size_t i = 0; i < arrayCount(euControlInterruptSeqno); i++) { euControlInterruptSeqno[i] = invalidHandle; } }; DebugSessionLinux::~DebugSessionLinux() { closeAsyncThread(); closeInternalEventsThread(); for (auto &session : tileSessions) { delete session.first; } tileSessions.resize(0); closeFd(); } DebugSession *DebugSession::create(const zet_debug_config_t &config, Device *device, ze_result_t &result, bool isRootAttach) { if (device->getOsInterface().isDebugAttachAvailable()) { struct prelim_drm_i915_debugger_open_param open = {}; open.pid = config.pid; open.events = 0; open.version = 0; auto debugFd = DrmHelper::ioctl(device, NEO::DrmIoctl::DebuggerOpen, &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, &open); debugSession->setAttachMode(isRootAttach); 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; } bool isRootDevice = !connectedDevice->getNEODevice()->isSubDevice(); if (isRootDevice && !tileAttachEnabled) { createEuThreads(); } createTileSessionsIfEnabled(); startInternalEventsThread(); bool allEventsCollected = false; bool eventAvailable = true; float timeDelta = 0; float timeStart = clock(); do { if (internalThreadHasStarted) { auto eventMemory = getInternalEvent(); auto debugEvent = reinterpret_cast(eventMemory.get()); if (eventMemory != nullptr) { handleEvent(debugEvent); if (debugEvent->type != PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND && pendingVmBindEvents.size() > 0) { processPendingVmBindEvents(); } eventAvailable = true; } else { eventAvailable = false; } allEventsCollected = checkAllEventsCollected(); } else { timeDelta = float(clock() - timeStart) / CLOCKS_PER_SEC; } } while ((eventAvailable && !allEventsCollected) && timeDelta < getThreadStartLimitTime()); internalThreadHasStarted = false; if (clientHandleClosed == clientHandle && clientHandle != invalidClientHandle) { return ZE_RESULT_ERROR_DEVICE_LOST; } if (allEventsCollected) { if (!readModuleDebugArea()) { return ZE_RESULT_ERROR_UNKNOWN; } return ZE_RESULT_SUCCESS; } return ZE_RESULT_NOT_READY; } void DebugSessionLinux::createTileSessionsIfEnabled() { auto numTiles = connectedDevice->getNEODevice()->getNumSubDevices(); if (numTiles > 0 && tileAttachEnabled) { tileSessions.resize(numTiles); for (uint32_t i = 0; i < numTiles; i++) { auto subDevice = connectedDevice->getNEODevice()->getSubDevice(i)->getSpecializedDevice(); tileSessions[i] = std::pair{createTileSession(config, subDevice, this), false}; } tileSessionsEnabled = true; } } TileDebugSessionLinux *DebugSessionLinux::createTileSession(const zet_debug_config_t &config, Device *device, DebugSessionImp *rootDebugSession) { auto tileSession = new TileDebugSessionLinux(config, device, rootDebugSession); tileSession->initialize(); return tileSession; } void *DebugSessionLinux::asyncThreadFunction(void *arg) { DebugSessionLinux *self = reinterpret_cast(arg); PRINT_DEBUGGER_INFO_LOG("Debugger async thread start\n", ""); while (self->asyncThread.threadActive) { self->handleEventsAsync(); if (self->tileSessionsEnabled) { for (size_t tileIndex = 0; tileIndex < self->tileSessions.size(); tileIndex++) { static_cast(self->tileSessions[tileIndex].first)->generateEventsAndResumeStoppedThreads(); static_cast(self->tileSessions[tileIndex].first)->sendInterrupts(); } } else { self->generateEventsAndResumeStoppedThreads(); self->sendInterrupts(); } } PRINT_DEBUGGER_INFO_LOG("Debugger async thread closing\n", ""); return nullptr; } void *DebugSessionLinux::readInternalEventsThreadFunction(void *arg) { DebugSessionLinux *self = reinterpret_cast(arg); PRINT_DEBUGGER_INFO_LOG("Debugger internal event thread started\n", ""); self->internalThreadHasStarted = true; while (self->internalEventThread.threadActive) { self->readInternalEventsAsync(); } PRINT_DEBUGGER_INFO_LOG("Debugger internal event thread closing\n", ""); return nullptr; } void DebugSessionLinux::startAsyncThread() { asyncThread.thread = NEO::Thread::create(asyncThreadFunction, reinterpret_cast(this)); } void DebugSessionLinux::closeAsyncThread() { asyncThread.close(); internalEventThread.close(); } bool DebugSessionLinux::closeFd() { if (fd == 0) { return false; } auto ret = NEO::SysCalls::close(fd); if (ret != 0) { PRINT_DEBUGGER_ERROR_LOG("Debug connection close() on fd: %d failed: retCode: %d\n", fd, ret); return false; } fd = 0; return true; } std::unique_ptr DebugSessionLinux::getInternalEvent() { std::unique_ptr eventMemory; { std::unique_lock lock(internalEventThreadMutex); if (internalEventQueue.empty()) { internalEventCondition.wait_for(lock, std::chrono::milliseconds(100)); } if (!internalEventQueue.empty()) { eventMemory = std::move(internalEventQueue.front()); internalEventQueue.pop(); } } return eventMemory; } void DebugSessionLinux::handleEventsAsync() { auto eventMemory = getInternalEvent(); if (eventMemory != nullptr) { auto debugEvent = reinterpret_cast(eventMemory.get()); handleEvent(debugEvent); if (debugEvent->type != PRELIM_DRM_I915_DEBUG_EVENT_VM_BIND && pendingVmBindEvents.size() > 0) { processPendingVmBindEvents(); } } } void DebugSessionLinux::readInternalEventsAsync() { struct pollfd pollFd = { .fd = fd, .events = POLLIN, .revents = 0, }; int pollTimeout = 1000; auto numberOfFds = ioctlHandler->poll(&pollFd, 1, pollTimeout); PRINT_DEBUGGER_INFO_LOG("Debugger async thread readEvent poll() retCode: %d\n", numberOfFds); if (!detached && numberOfFds < 0 && errno == EINVAL) { zet_debug_event_t debugEvent = {}; debugEvent.type = ZET_DEBUG_EVENT_TYPE_DETACHED; debugEvent.info.detached.reason = ZET_DEBUG_DETACH_REASON_INVALID; PRINT_DEBUGGER_INFO_LOG("Debugger detached\n", ""); if (tileSessionsEnabled) { auto numTiles = connectedDevice->getNEODevice()->getNumSubDevices(); for (uint32_t tileIndex = 0; tileIndex < numTiles; tileIndex++) { static_cast(tileSessions[tileIndex].first)->pushApiEvent(debugEvent); static_cast(tileSessions[tileIndex].first)->detached = true; } } else { pushApiEvent(debugEvent); } detached = true; } else if (numberOfFds > 0) { ze_result_t result = ZE_RESULT_SUCCESS; int maxLoopCount = 3; do { 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; result = readEventImp(event); maxLoopCount--; if (result == ZE_RESULT_SUCCESS) { std::lock_guard lock(internalEventThreadMutex); auto memory = std::make_unique(maxEventSize / sizeof(uint64_t)); memcpy(memory.get(), event, maxEventSize); internalEventQueue.push(std::move(memory)); internalEventCondition.notify_one(); } } while (result == ZE_RESULT_SUCCESS && maxLoopCount > 0); } } bool DebugSessionLinux::closeConnection() { closeAsyncThread(); closeInternalEventsThread(); if (clientHandle != invalidClientHandle) { auto numTiles = std::max(1u, connectedDevice->getNEODevice()->getNumSubDevices()); for (uint32_t i = 0; i < numTiles; i++) { for (const auto &eventToAck : eventsToAck) { auto moduleUUID = eventToAck.second; ackModuleEvents(i, moduleUUID); } cleanRootSessionAfterDetach(i); } } return closeFd(); } 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] != 0 || clientHandleToConnection[uuid->client_handle]->uuidToModule[uuid->handle].loadAddresses[0].size() > 0); clientHandleToConnection[uuid->client_handle]->uuidToModule.erase(uuid->handle); } if (destroy && (clientHandle == uuid->client_handle)) { for (const auto &uuidToDevice : uuidL0CommandQueueHandleToDevice) { if (uuidToDevice.first == uuid->handle) { auto deviceIndex = uuidToDevice.second; uuidL0CommandQueueHandleToDevice.erase(uuidToDevice.first); zet_debug_event_t debugEvent = {}; debugEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT; if (tileSessionsEnabled) { auto tileSession = reinterpret_cast(tileSessions[deviceIndex].first); tileSession->processExit(); tileSession->pushApiEvent(debugEvent); } else if (uuidL0CommandQueueHandleToDevice.size() == 0) { pushApiEvent(debugEvent); } break; } } 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 ret = ioctl(PRELIM_I915_DEBUG_IOCTL_READ_UUID, &readUuid); if (ret == 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; uint32_t deviceIndex = 0; if (readUuid.payload_size == sizeof(NEO::DebuggerL0::CommandQueueNotification)) { auto notification = reinterpret_cast(payload.get()); deviceIndex = notification->subDeviceIndex; UNRECOVERABLE_IF(notification->subDeviceCount > 0 && notification->subDeviceIndex >= notification->subDeviceCount); } zet_debug_event_t debugEvent = {}; debugEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY; if (tileSessionsEnabled) { UNRECOVERABLE_IF(uuidL0CommandQueueHandleToDevice.find(uuid->handle) != uuidL0CommandQueueHandleToDevice.end()); auto tileSession = static_cast(tileSessions[deviceIndex].first); tileSession->processEntry(); tileSession->pushApiEvent(debugEvent); } else if (uuidL0CommandQueueHandleToDevice.size() == 0) { pushApiEvent(debugEvent); } uuidL0CommandQueueHandleToDevice[uuid->handle] = deviceIndex; } } 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.moduleUuidHandle = handle; for (uint32_t i = 0; i < NEO::EngineLimits::maxHandleCount; i++) { newModule.segmentVmBindCounter[i] = 0; newModule.loadAddresses[i].clear(); newModule.moduleLoadEventAcked[i] = false; } } extractUuidData(uuid->client_handle, uuidData); } PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_UUID client_handle = %llu handle = %llu flags = %d uuid = %s ret = %d\n", (uint64_t)readUuid.client_handle, (uint64_t)readUuid.handle, (int)readUuid.flags, uuidString.c_str(), ret); } else { PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_READ_UUID ret = %d errno = %d\n", ret, 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); if (!handleVmBindEvent(vmBind)) { if (event->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); } else { auto sizeAligned = alignUp(event->size, sizeof(uint64_t)); auto pendingEvent = std::make_unique(sizeAligned / sizeof(uint64_t)); memcpy_s(pendingEvent.get(), sizeAligned, event, event->size); pendingVmBindEvents.push_back(std::move(pendingEvent)); } } } 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; case PRELIM_DRM_I915_DEBUG_EVENT_PAGE_FAULT: { prelim_drm_i915_debug_event_page_fault *pf = reinterpret_cast(event); PRINT_DEBUGGER_INFO_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_I915_DEBUG_EVENT_PAGE_FAULT flags = %d, address = %llu seqno = %d, size = %llu" " client_handle = %llu flags = %llu class = %lu instance = %lu bitmask_size = %lu ctx_handle = %llu\n", (int)pf->base.flags, (uint64_t)pf->page_fault_address, (uint64_t)pf->base.seqno, (uint64_t)pf->base.size, (uint64_t)pf->client_handle, (uint64_t)pf->flags, (uint32_t)pf->ci.engine_class, (uint32_t)pf->ci.engine_instance, (uint32_t)pf->bitmask_size, uint64_t(pf->ctx_handle)); handlePageFaultEvent(pf); } 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; } } void DebugSessionLinux::processPendingVmBindEvents() { size_t processedEvents = 0; for (size_t index = 0; index < pendingVmBindEvents.size(); index++) { auto debugEvent = reinterpret_cast(pendingVmBindEvents[index].get()); if (handleVmBindEvent(debugEvent) == false) { break; } processedEvents++; } if (processedEvents > 0) { pendingVmBindEvents.erase(pendingVmBindEvents.begin(), pendingVmBindEvents.begin() + processedEvents); } } bool DebugSessionLinux::checkAllEventsCollected() { bool allEventsCollected = false; bool clientConnected = (this->clientHandle != invalidClientHandle); if (clientConnected) { if (clientHandleToConnection[clientHandle]->vmToModuleDebugAreaBindInfo.size() > 0) { allEventsCollected = true; } } PRINT_DEBUGGER_INFO_LOG("checkAllEventsCollected() returned %d, clientHandle = %ull\n", static_cast(allEventsCollected), this->clientHandle); 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(NEO::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::lock_guard 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; } else { validateAndSetStateSaveAreaHeader(vm, gpuVa); } } } ze_result_t DebugSessionLinux::readEventImp(prelim_drm_i915_debug_event *drmDebugEvent) { auto ret = ioctl(PRELIM_I915_DEBUG_IOCTL_READ_EVENT, drmDebugEvent); if (ret != 0) { PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT failed: retCode: %d errno = %d\n", ret, errno); return ZE_RESULT_NOT_READY; } else if (drmDebugEvent->flags & ~static_cast(PRELIM_DRM_I915_DEBUG_EVENT_CREATE | PRELIM_DRM_I915_DEBUG_EVENT_DESTROY | PRELIM_DRM_I915_DEBUG_EVENT_STATE_CHANGE | PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK)) { PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_READ_EVENT unsupported flag = %d\n", (int)drmDebugEvent->flags); return ZE_RESULT_ERROR_UNKNOWN; } return ZE_RESULT_SUCCESS; } bool 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; auto connection = clientHandleToConnection[vmBind->client_handle].get(); uint32_t index = 0; const 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 false; } if (connection->vmToTile.find(vmHandle) == connection->vmToTile.end()) { DEBUG_BREAK_IF(connection->vmToTile.find(vmHandle) == connection->vmToTile.end() && (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK) && (connection->uuidMap[uuid].classIndex == NEO::DrmResourceClass::Isa || connection->uuidMap[uuid].classIndex == NEO::DrmResourceClass::ModuleHeapDebugArea)); return false; } const auto tileIndex = connection->vmToTile[vmHandle]; PRINT_DEBUGGER_INFO_LOG("UUID handle = %llu class index = %d\n", static_cast(vmBind->uuids[index]), static_cast(clientHandleToConnection[vmBind->client_handle]->uuidMap[vmBind->uuids[index]].classIndex)); auto classUuid = connection->uuidMap[uuid].classHandle; if (connection->classHandleToIndex.find(classUuid) != connection->classHandleToIndex.end()) { std::lock_guard 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}; } } bool handleEvent = isTileWithinDeviceBitfield(tileIndex); if (handleEvent && connection->uuidMap[uuid].classIndex == NEO::DrmResourceClass::Isa) { uint32_t deviceBitfield = 0; memcpy_s(&deviceBitfield, sizeof(uint32_t), connection->uuidMap[uuid].data.get(), connection->uuidMap[uuid].dataSize); const NEO::DeviceBitfield devices(deviceBitfield); PRINT_DEBUGGER_INFO_LOG("ISA vm_handle = %llu, tileIndex = %lu, deviceBitfield = %llu", vmHandle, tileIndex, devices.to_ulong()); const auto isaUuidHandle = connection->uuidMap[uuid].handle; bool perKernelModules = true; int moduleUUIDindex = -1; bool tileInstanced = false; bool allInstancesEventsReceived = true; 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); PRINT_DEBUGGER_INFO_LOG("Zebin module uuid = %ull", (uint64_t)vmBind->uuids[uuidIter]); } if (connection->uuidMap[vmBind->uuids[uuidIter]].classHandle == isaUuidHandle) { tileInstanced = true; } } if (connection->isaMap[tileIndex].find(vmBind->va_start) == connection->isaMap[tileIndex].end() && createEvent) { auto &isaMap = connection->isaMap[tileIndex]; auto &elfMap = connection->elfMap; 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; isa->tileInstanced = tileInstanced; isa->perKernelModule = perKernelModules; isa->deviceBitfield = devices; 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]; module.deviceBitfield = devices; } } } 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", ""); } auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); auto loadAddress = gmmHelper->canonize(vmBind->va_start); 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); // Expect non canonical va_start DEBUG_BREAK_IF(gmmHelper->decanonize(vmBind->va_start) != vmBind->va_start); bool apiEventNeedsAck = (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK); // If ACK flag is not set when triggering MODULE LOAD event, auto-ack immediately if (apiEventNeedsAck == false) { isaMap[vmBind->va_start]->moduleLoadEventAck = true; } if (tileSessionsEnabled) { auto tileAttached = tileSessions[tileIndex].second; if (!tileAttached) { isaMap[vmBind->va_start]->moduleLoadEventAck = true; apiEventNeedsAck = false; } PRINT_DEBUGGER_INFO_LOG("TileDebugSession attached = %d, tileIndex = %lu, apiEventNeedsAck = %d", (int)tileAttached, tileIndex, (int)apiEventNeedsAck); } memLock.unlock(); if (perKernelModules) { PRINT_DEBUGGER_INFO_LOG("New per-kernel module\n", ""); debugEvent.flags = apiEventNeedsAck ? ZET_DEBUG_EVENT_FLAG_NEED_ACK : 0; if (tileSessionsEnabled) { auto tileAttached = static_cast(tileSessions[tileIndex].first)->insertModule(debugEvent.info.module); if (tileAttached) { static_cast(tileSessions[tileIndex].first)->pushApiEvent(debugEvent); } } else { if (devices.count() > 1) { allInstancesEventsReceived = checkAllOtherTileIsaAllocationsPresent(tileIndex, vmBind->va_start); } if (allInstancesEventsReceived) { pushApiEvent(debugEvent); } } } } if (createEvent) { std::lock_guard lock(asyncThreadMutex); if (!connection->isaMap[tileIndex][vmBind->va_start]->moduleLoadEventAck && perKernelModules) { bool doNotAutoAckEvent = (!blockOnFenceMode && allInstancesEventsReceived); // in block on CPU mode - do not auto-ack last event for isa instance doNotAutoAckEvent |= blockOnFenceMode; // in block on fence mode - do not auto-ack any events if (doNotAutoAckEvent) { PRINT_DEBUGGER_INFO_LOG("Add event to ack, seqno = %llu", (uint64_t)vmBind->base.seqno); connection->isaMap[tileIndex][vmBind->va_start]->ackEvents.push_back(vmBind->base); shouldAckEvent = false; } } connection->isaMap[tileIndex][vmBind->va_start]->vmBindCounter++; } if (destroyEvent && connection->isaMap[tileIndex].find(vmBind->va_start) != connection->isaMap[tileIndex].end()) { DEBUG_BREAK_IF(connection->isaMap[tileIndex][vmBind->va_start]->vmBindCounter == 0); connection->isaMap[tileIndex][vmBind->va_start]->vmBindCounter--; if (connection->isaMap[tileIndex][vmBind->va_start]->vmBindCounter == 0) { const auto &isa = connection->isaMap[tileIndex][vmBind->va_start]; zet_debug_event_t debugEvent = {}; auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); auto loadAddress = gmmHelper->canonize(isa->bindInfo.gpuVa); 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 = isa->moduleBegin; debugEvent.info.module.moduleEnd = isa->moduleEnd; if (perKernelModules) { if (tileSessionsEnabled) { static_cast(tileSessions[tileIndex].first)->removeModule(debugEvent.info.module); static_cast(tileSessions[tileIndex].first)->pushApiEvent(debugEvent); } else { bool notifyEvent = true; if (isa->deviceBitfield.count() > 1) { notifyEvent = checkAllOtherTileIsaAllocationsRemoved(tileIndex, vmBind->va_start); } if (notifyEvent) { pushApiEvent(debugEvent); } } } std::unique_lock memLock(asyncThreadMutex); connection->isaMap[tileIndex].erase(vmBind->va_start); memLock.unlock(); } } } if (handleEvent) { 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]]; auto moduleUsedOnTile = module.deviceBitfield.test(tileIndex) || module.deviceBitfield.count() == 0; if (moduleUsedOnTile) { if (createEvent) { module.segmentVmBindCounter[tileIndex]++; DEBUG_BREAK_IF(module.loadAddresses[tileIndex].size() > module.segmentCount); bool canTriggerEvent = module.loadAddresses[tileIndex].size() == (module.segmentCount - 1); module.loadAddresses[tileIndex].insert(vmBind->va_start); if (!blockOnFenceMode) { if (canTriggerEvent && module.loadAddresses[tileIndex].size() == module.segmentCount) { auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); loadAddress = gmmHelper->canonize(*std::min_element(module.loadAddresses[tileIndex].begin(), module.loadAddresses[tileIndex].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; if (!tileSessionsEnabled) { bool allInstancesEventsReceived = true; if (module.deviceBitfield.count() > 1) { allInstancesEventsReceived = checkAllOtherTileModuleSegmentsPresent(tileIndex, module); } if (allInstancesEventsReceived) { if (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK) { debugEvent.flags = ZET_DEBUG_EVENT_FLAG_NEED_ACK; module.ackEvents[tileIndex].push_back(vmBind->base); } pushApiEvent(debugEvent, vmBind->uuids[uuidIter]); shouldAckEvent = false; } } else { auto tileAttached = static_cast(tileSessions[tileIndex].first)->insertModule(debugEvent.info.module); if (tileAttached) { if (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK) { debugEvent.flags = ZET_DEBUG_EVENT_FLAG_NEED_ACK; module.ackEvents[tileIndex].push_back(vmBind->base); } static_cast(tileSessions[tileIndex].first)->pushApiEvent(debugEvent, vmBind->uuids[uuidIter]); shouldAckEvent = false; } } } } else { PRINT_DEBUGGER_INFO_LOG("Zebin module = %ull has load addresses = %d", static_cast(vmBind->uuids[uuidIter]), static_cast(module.loadAddresses[tileIndex].size())); if (canTriggerEvent && module.loadAddresses[tileIndex].size() == module.segmentCount) { auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); loadAddress = gmmHelper->canonize(*std::min_element(module.loadAddresses[tileIndex].begin(), module.loadAddresses[tileIndex].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; if (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK) { debugEvent.flags = ZET_DEBUG_EVENT_FLAG_NEED_ACK; } if (!tileSessionsEnabled) { bool allInstancesEventsReceived = true; if (module.deviceBitfield.count() > 1) { allInstancesEventsReceived = checkAllOtherTileModuleSegmentsPresent(tileIndex, module); } if (allInstancesEventsReceived) { pushApiEvent(debugEvent, vmBind->uuids[uuidIter]); shouldAckEvent = false; } } else { auto tileAttached = static_cast(tileSessions[tileIndex].first)->insertModule(debugEvent.info.module); if (tileAttached) { static_cast(tileSessions[tileIndex].first)->pushApiEvent(debugEvent, vmBind->uuids[uuidIter]); shouldAckEvent = false; } } } { std::lock_guard lock(asyncThreadMutex); if (!module.moduleLoadEventAcked[tileIndex]) { shouldAckEvent = false; } if (tileSessionsEnabled && !static_cast(tileSessions[tileIndex].first)->isAttached) { shouldAckEvent = true; } if (!shouldAckEvent && (vmBind->base.flags & PRELIM_DRM_I915_DEBUG_EVENT_NEED_ACK)) { module.ackEvents[tileIndex].push_back(vmBind->base); } } } } else { // destroyEvent module.segmentVmBindCounter[tileIndex]--; if (module.segmentVmBindCounter[tileIndex] == 0) { zet_debug_event_t debugEvent = {}; auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); auto loadAddress = gmmHelper->canonize(*std::min_element(module.loadAddresses[tileIndex].begin(), module.loadAddresses[tileIndex].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; if (tileSessionsEnabled) { auto tileAttached = static_cast(tileSessions[tileIndex].first)->removeModule(debugEvent.info.module); if (tileAttached) { static_cast(tileSessions[tileIndex].first)->pushApiEvent(debugEvent, vmBind->uuids[uuidIter]); } } else { bool notifyEvent = true; if (module.deviceBitfield.count() > 1) { notifyEvent = checkAllOtherTileModuleSegmentsRemoved(tileIndex, module); } if (notifyEvent) { pushApiEvent(debugEvent, vmBind->uuids[uuidIter]); } } module.loadAddresses[tileIndex].clear(); module.moduleLoadEventAcked[tileIndex] = false; } } } break; } } } } 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); } return true; } 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", (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]); } auto vm = clientHandleToConnection[contextParam->client_handle]->contextsCreated[contextParam->ctx_handle].vm; if (numEngines && vm != invalidHandle) { NEO::EngineClassInstance engineClassInstance = {engines->engines[0].engine_class, engines->engines[0].engine_instance}; auto tileIndex = DrmHelper::getEngineTileIndex(connectedDevice, engineClassInstance); clientHandleToConnection[contextParam->client_handle]->vmToTile[vm] = tileIndex; PRINT_DEBUGGER_INFO_LOG("VM = %llu mapped to TILE = %lu\n", vm, tileIndex); } break; } default: PRINT_DEBUGGER_INFO_LOG("I915_CONTEXT_PARAM UNHANDLED = %llu\n", (uint64_t)contextParam->param.param); break; } } uint64_t DebugSessionLinux::getVmHandleFromClientAndlrcHandle(uint64_t clientHandle, uint64_t lrcHandle) { if (clientHandleToConnection.find(clientHandle) == clientHandleToConnection.end()) { return invalidHandle; } auto &clientConnection = clientHandleToConnection[clientHandle]; if (clientConnection->lrcToContextHandle.find(lrcHandle) == clientConnection->lrcToContextHandle.end()) { return invalidHandle; } auto contextHandle = clientConnection->lrcToContextHandle[lrcHandle]; if (clientConnection->contextsCreated.find(contextHandle) == clientConnection->contextsCreated.end()) { return invalidHandle; } return clientConnection->contextsCreated[contextHandle].vm; } 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); auto vmHandle = getVmHandleFromClientAndlrcHandle(attention->client_handle, attention->lrc_handle); if (vmHandle == invalidHandle) { return; } if (!connectedDevice->getNEODevice()->getDeviceBitfield().test(tileIndex)) { return; } auto hwInfo = connectedDevice->getHwInfo(); auto &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper(); std::vector threadsWithAttention; if (interruptSent) { std::unique_ptr bitmask; size_t bitmaskSize; auto attReadResult = threadControl({}, tileIndex, ThreadControlCmd::Stopped, bitmask, bitmaskSize); if (attReadResult == 0) { threadsWithAttention = l0GfxCoreHelper.getThreadsFromAttentionBitmask(hwInfo, tileIndex, bitmask.get(), bitmaskSize); } } if (threadsWithAttention.size() == 0) { threadsWithAttention = l0GfxCoreHelper.getThreadsFromAttentionBitmask(hwInfo, tileIndex, 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()); if (threadsWithAttention.size() > 0) { auto gpuVa = getContextStateSaveAreaGpuVa(vmHandle); auto stateSaveAreaSize = getContextStateSaveAreaSize(vmHandle); auto stateSaveReadResult = ZE_RESULT_ERROR_UNKNOWN; std::unique_lock lock; if (tileSessionsEnabled) { lock = std::unique_lock(static_cast(tileSessions[tileIndex].first)->threadStateMutex); } else { lock = std::unique_lock(threadStateMutex); } if (gpuVa != 0 && stateSaveAreaSize != 0) { std::vector newThreads; getNotStoppedThreads(threadsWithAttention, newThreads); if (newThreads.size() > 0) { allocateStateSaveAreaMemory(stateSaveAreaSize); stateSaveReadResult = readGpuMemory(vmHandle, stateSaveAreaMemory.data(), stateSaveAreaSize, gpuVa); } } else { PRINT_DEBUGGER_ERROR_LOG("Context state save area bind info invalid\n", ""); DEBUG_BREAK_IF(true); } if (stateSaveReadResult == ZE_RESULT_SUCCESS) { for (auto &threadId : threadsWithAttention) { PRINT_DEBUGGER_THREAD_LOG("ATTENTION event for thread: %s\n", EuThread::toString(threadId).c_str()); if (tileSessionsEnabled) { static_cast(tileSessions[tileIndex].first)->addThreadToNewlyStoppedFromRaisedAttention(threadId, vmHandle, stateSaveAreaMemory.data()); } else { addThreadToNewlyStoppedFromRaisedAttention(threadId, vmHandle, stateSaveAreaMemory.data()); } } } } if (tileSessionsEnabled) { static_cast(tileSessions[tileIndex].first)->checkTriggerEventsForAttention(); } else { checkTriggerEventsForAttention(); } } void DebugSessionLinux::handlePageFaultEvent(prelim_drm_i915_debug_event_page_fault *pf) { NEO::EngineClassInstance engineClassInstance = {pf->ci.engine_class, pf->ci.engine_instance}; auto tileIndex = DrmHelper::getEngineTileIndex(connectedDevice, engineClassInstance); DEBUG_BREAK_IF(pf->bitmask_size % 3u != 0u); size_t size = pf->bitmask_size / 3; uint8_t *bitmaskBefore = &pf->bitmask[0]; uint8_t *bitmaskAfter = &pf->bitmask[size]; uint8_t *bitmaskResolved = &pf->bitmask[size * 2]; PRINT_DEBUGGER_INFO_LOG("PageFault event BEFORE", 0); printBitmask(bitmaskBefore, size); PRINT_DEBUGGER_INFO_LOG("PageFault event AFTER", 0); printBitmask(bitmaskAfter, size); PRINT_DEBUGGER_INFO_LOG("PageFault event RESOLVED", 0); printBitmask(bitmaskResolved, size); auto vmHandle = getVmHandleFromClientAndlrcHandle(pf->client_handle, pf->lrc_handle); if (vmHandle == invalidHandle) { return; } if (!connectedDevice->getNEODevice()->getDeviceBitfield().test(tileIndex)) { return; } std::unique_ptr bitmaskPF = std::make_unique(size); std::transform(bitmaskAfter, bitmaskAfter + size, bitmaskResolved, bitmaskPF.get(), std::bit_xor()); auto hwInfo = connectedDevice->getHwInfo(); auto &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper(); auto threadsWithPF = l0GfxCoreHelper.getThreadsFromAttentionBitmask(hwInfo, tileIndex, bitmaskPF.get(), size); auto stoppedThreads = l0GfxCoreHelper.getThreadsFromAttentionBitmask(hwInfo, tileIndex, bitmaskResolved, size); if (threadsWithPF.size() == 0) { zet_debug_event_t debugEvent = {}; debugEvent.type = ZET_DEBUG_EVENT_TYPE_PAGE_FAULT; PRINT_DEBUGGER_INFO_LOG("PageFault event for unknown thread", 0); enqueueApiEvent(debugEvent); } auto gpuVa = getContextStateSaveAreaGpuVa(vmHandle); auto stateSaveAreaSize = getContextStateSaveAreaSize(vmHandle); allocateStateSaveAreaMemory(stateSaveAreaSize); auto stateSaveReadResult = readGpuMemory(vmHandle, stateSaveAreaMemory.data(), stateSaveAreaSize, gpuVa); if (stateSaveReadResult == ZE_RESULT_SUCCESS) { std::unique_lock lock; if (tileSessionsEnabled) { lock = std::unique_lock(static_cast(tileSessions[tileIndex].first)->threadStateMutex); } else { lock = std::unique_lock(threadStateMutex); } for (auto threadId : threadsWithPF) { PRINT_DEBUGGER_INFO_LOG("PageFault event for thread %s", EuThread::toString(threadId).c_str()); allThreads[threadId]->setPageFault(true); } for (auto threadId : stoppedThreads) { if (tileSessionsEnabled) { static_cast(tileSessions[tileIndex].first)->addThreadToNewlyStoppedFromRaisedAttention(threadId, vmHandle, stateSaveAreaMemory.data()); } else { addThreadToNewlyStoppedFromRaisedAttention(threadId, vmHandle, stateSaveAreaMemory.data()); } } } if (tileSessionsEnabled) { static_cast(tileSessions[tileIndex].first)->checkTriggerEventsForAttention(); } else { checkTriggerEventsForAttention(); } return; } 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; PRINT_DEBUGGER_INFO_LOG("Zebin module = %ull, segment count = %ul", uuidData.handle, 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(const 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 &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper(); 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) { l0GfxCoreHelper.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(bitmask.get(), bitmaskSize); } printBitmask(bitmask.get(), bitmaskSize); auto euControlRetVal = ioctl(PRELIM_I915_DEBUG_IOCTL_EU_CONTROL, &euControl); if (euControlRetVal != 0) { PRINT_DEBUGGER_ERROR_LOG("PRELIM_I915_DEBUG_IOCTL_EU_CONTROL failed: retCode: %d errno = %d command = %d\n", euControlRetVal, 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 (euControlRetVal == 0) { euControlInterruptSeqno[tile] = euControl.seqno; } else { euControlInterruptSeqno[tile] = invalidHandle; } } if (threadCmd == ThreadControlCmd::Stopped) { bitmaskOut = std::move(bitmask); bitmaskSizeOut = euControl.bitmask_size; } return euControlRetVal; } void DebugSessionLinux::checkStoppedThreadsAndGenerateEvents(const std::vector &threads, uint64_t memoryHandle, uint32_t deviceIndex) { std::vector threadsWithAttention; std::vector stoppedThreadsToReport; NEO::sleep(std::chrono::microseconds(1)); if (threads.size() > 1) { auto hwInfo = connectedDevice->getHwInfo(); auto &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper(); std::unique_ptr bitmask; size_t bitmaskSize; [[maybe_unused]] auto attReadResult = threadControl(threads, deviceIndex, ThreadControlCmd::Stopped, bitmask, bitmaskSize); // error querying STOPPED threads - no threads available ( for example: threads have completed ) if (attReadResult != 0) { PRINT_DEBUGGER_ERROR_LOG("checkStoppedThreadsAndGenerateEvents ATTENTION read failed: %d errno = %d \n", (int)attReadResult, DrmHelper::getErrno(connectedDevice)); return; } threadsWithAttention = l0GfxCoreHelper.getThreadsFromAttentionBitmask(hwInfo, deviceIndex, bitmask.get(), bitmaskSize); if (threadsWithAttention.size() == 0) { return; } } const auto &threadsToCheck = threadsWithAttention.size() > 0 ? threadsWithAttention : threads; stoppedThreadsToReport.reserve(threadsToCheck.size()); for (auto &threadId : threadsToCheck) { SIP::sr_ident srMagic = {{0}}; srMagic.count = 0; if (readSystemRoutineIdent(allThreads[threadId].get(), memoryHandle, srMagic)) { bool wasStopped = allThreads[threadId]->isStopped(); if (allThreads[threadId]->verifyStopped(srMagic.count)) { allThreads[threadId]->stopThread(memoryHandle); if (!wasStopped) { stoppedThreadsToReport.push_back(threadId); } } } else { break; } } generateEventsForStoppedThreads(stoppedThreadsToReport); } ze_result_t DebugSessionLinux::resumeImp(const std::vector &threads, uint32_t deviceIndex) { std::unique_ptr bitmask; size_t bitmaskSize; auto result = threadControl(threads, deviceIndex, ThreadControlCmd::Resume, bitmask, bitmaskSize); return result == 0 ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_NOT_AVAILABLE; } ze_result_t DebugSessionLinux::interruptImp(uint32_t deviceIndex) { std::unique_ptr bitmask; size_t bitmaskSize; auto result = threadControl({}, deviceIndex, ThreadControlCmd::InterruptAll, bitmask, bitmaskSize); return result == 0 ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_NOT_AVAILABLE; } ze_result_t DebugSessionLinux::getISAVMHandle(uint32_t deviceIndex, const zet_debug_memory_space_desc_t *desc, size_t size, uint64_t &vmHandle) { auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); auto accessVA = gmmHelper->decanonize(desc->address); auto &isaMap = clientHandleToConnection[clientHandle]->isaMap[deviceIndex]; 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; } bool DebugSessionLinux::getIsaInfoForAllInstances(NEO::DeviceBitfield deviceBitfield, const zet_debug_memory_space_desc_t *desc, size_t size, uint64_t vmHandles[], ze_result_t &status) { auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); auto accessVA = gmmHelper->decanonize(desc->address); status = ZE_RESULT_ERROR_UNINITIALIZED; bool tileInstancedIsa = false; bool invalidIsaRange = false; uint32_t isaFound = 0; for (uint32_t i = 0; i < NEO::EngineLimits::maxHandleCount; i++) { vmHandles[i] = invalidHandle; if (deviceBitfield.test(i)) { auto &isaMap = clientHandleToConnection[clientHandle]->isaMap[i]; 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) { isaFound++; if (accessVA + size > ceilVa) { invalidIsaRange = true; } else { vmHandles[i] = isa.second->vmHandle; } tileInstancedIsa = isa.second->tileInstanced; break; } } } } } if (invalidIsaRange) { status = ZE_RESULT_ERROR_INVALID_ARGUMENT; } else if (isaFound > 0) { if ((tileInstancedIsa && deviceBitfield.count() == isaFound) || !tileInstancedIsa) { status = ZE_RESULT_SUCCESS; } } return isaFound > 0; } 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); } } } bool DebugSessionLinux::tryReadElf(const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer, ze_result_t &status) { const char *elfData = nullptr; uint64_t offset = 0; std::lock_guard memLock(asyncThreadMutex); status = getElfOffset(desc, size, elfData, offset); if (status == ZE_RESULT_ERROR_INVALID_ARGUMENT) { return true; } if (elfData) { status = readElfSpace(desc, size, buffer, elfData, offset); return true; } return false; } 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) { ze_result_t status = validateThreadAndDescForMemoryAccess(thread, desc); if (status != ZE_RESULT_SUCCESS) { return status; } if (desc->type == ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT) { status = readDefaultMemory(thread, desc, size, buffer); } else { auto threadId = convertToThreadId(thread); status = slmMemoryAccess(threadId, desc, size, buffer); } return status; } ze_result_t DebugSessionLinux::readDefaultMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer) { ze_result_t status = ZE_RESULT_SUCCESS; bool isa = tryReadIsa(connectedDevice->getNEODevice()->getDeviceBitfield(), desc, size, buffer, status); if (isa) { return status; } bool elf = tryReadElf(desc, size, buffer, status); if (elf) { return status; } if (DebugSession::isThreadAll(thread)) { return accessDefaultMemForThreadAll(desc, size, const_cast(buffer), false); } auto threadId = convertToThreadId(thread); auto 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) { ze_result_t status = validateThreadAndDescForMemoryAccess(thread, desc); if (status != ZE_RESULT_SUCCESS) { return status; } if (desc->type == ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT) { status = writeDefaultMemory(thread, desc, size, buffer); } else { auto threadId = convertToThreadId(thread); status = slmMemoryAccess(threadId, desc, size, buffer); } return status; } ze_result_t DebugSessionLinux::writeDefaultMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, const void *buffer) { ze_result_t status = ZE_RESULT_SUCCESS; auto deviceBitfield = connectedDevice->getNEODevice()->getDeviceBitfield(); bool isa = tryWriteIsa(deviceBitfield, desc, size, buffer, status); if (isa) { return status; } if (DebugSession::isThreadAll(thread)) { return accessDefaultMemForThreadAll(desc, size, const_cast(buffer), true); } auto threadId = convertToThreadId(thread); auto threadVmHandle = allThreads[threadId]->getMemoryHandle(); if (threadVmHandle == invalidHandle) { return ZE_RESULT_ERROR_NOT_AVAILABLE; } return writeGpuMemory(threadVmHandle, static_cast(buffer), size, desc->address); } bool DebugSessionLinux::tryWriteIsa(NEO::DeviceBitfield deviceBitfield, const zet_debug_memory_space_desc_t *desc, size_t size, const void *buffer, ze_result_t &status) { return tryAccessIsa(deviceBitfield, desc, size, const_cast(buffer), true, status); } bool DebugSessionLinux::tryReadIsa(NEO::DeviceBitfield deviceBitfield, const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer, ze_result_t &status) { return tryAccessIsa(deviceBitfield, desc, size, buffer, false, status); } bool DebugSessionLinux::tryAccessIsa(NEO::DeviceBitfield deviceBitfield, const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer, bool write, ze_result_t &status) { status = ZE_RESULT_ERROR_NOT_AVAILABLE; uint64_t vmHandle[NEO::EngineLimits::maxHandleCount] = {invalidHandle}; uint32_t deviceIndex = Math::getMinLsbSet(static_cast(deviceBitfield.to_ulong())); bool isaAccess = false; auto checkIfAnyFailed = [](const auto &result) { return result != ZE_RESULT_SUCCESS; }; { std::lock_guard memLock(asyncThreadMutex); if (deviceBitfield.count() == 1) { status = getISAVMHandle(deviceIndex, desc, size, vmHandle[deviceIndex]); if (status == ZE_RESULT_SUCCESS) { isaAccess = true; } if (status == ZE_RESULT_ERROR_INVALID_ARGUMENT) { return true; } } else { isaAccess = getIsaInfoForAllInstances(deviceBitfield, desc, size, vmHandle, status); } } if (isaAccess && status == ZE_RESULT_SUCCESS) { if (write) { if (deviceBitfield.count() == 1) { if (vmHandle[deviceIndex] != invalidHandle) { status = writeGpuMemory(vmHandle[deviceIndex], static_cast(buffer), size, desc->address); } else { status = ZE_RESULT_ERROR_UNINITIALIZED; } } else { std::vector results(NEO::EngineLimits::maxHandleCount); for (uint32_t i = 0; i < NEO::EngineLimits::maxHandleCount; i++) { results[i] = ZE_RESULT_SUCCESS; if (deviceBitfield.test(i) && vmHandle[i] != invalidHandle) { results[i] = writeGpuMemory(vmHandle[i], static_cast(buffer), size, desc->address); if (results[i] != ZE_RESULT_SUCCESS) { break; } } } const bool anyFailed = std::any_of(results.begin(), results.end(), checkIfAnyFailed); if (anyFailed) { status = ZE_RESULT_ERROR_UNKNOWN; } } } else { if (deviceBitfield.count() > 1) { for (uint32_t i = 0; i < NEO::EngineLimits::maxHandleCount; i++) { if (vmHandle[i] != invalidHandle) { deviceIndex = i; break; } } } if (vmHandle[deviceIndex] != invalidHandle) { status = readGpuMemory(vmHandle[deviceIndex], static_cast(buffer), size, desc->address); } else { status = ZE_RESULT_ERROR_UNINITIALIZED; } } } return isaAccess; } ze_result_t DebugSessionLinux::accessDefaultMemForThreadAll(const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer, bool write) { auto status = ZE_RESULT_ERROR_UNINITIALIZED; std::vector allVms; allVms = getAllMemoryHandles(); if (allVms.size() > 0) { for (auto vmHandle : allVms) { if (write) { status = writeGpuMemory(vmHandle, static_cast(buffer), size, desc->address); } else { status = readGpuMemory(vmHandle, static_cast(buffer), size, desc->address); } if (status == ZE_RESULT_SUCCESS) { return status; } } status = ZE_RESULT_ERROR_NOT_AVAILABLE; } return status; } bool DebugSessionLinux::ackIsaEvents(uint32_t deviceIndex, uint64_t isaVa) { std::lock_guard lock(asyncThreadMutex); auto connection = clientHandleToConnection[clientHandle].get(); auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); auto isaVaStart = gmmHelper->decanonize(isaVa); auto isa = connection->isaMap[deviceIndex].find(isaVaStart); if (isa != connection->isaMap[deviceIndex].end()) { // zebin modules do not store ackEvents per ISA UNRECOVERABLE_IF(isa->second->ackEvents.size() > 0 && isa->second->perKernelModule == false); 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); } isa->second->ackEvents.clear(); isa->second->moduleLoadEventAck = true; return true; } return false; } bool DebugSessionLinux::ackModuleEvents(uint32_t deviceIndex, uint64_t moduleUuidHandle) { std::lock_guard lock(asyncThreadMutex); auto connection = clientHandleToConnection[clientHandle].get(); if (connection->uuidToModule.find(moduleUuidHandle) != connection->uuidToModule.end()) { auto &module = connection->uuidToModule[moduleUuidHandle]; for (auto &event : module.ackEvents[deviceIndex]) { 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); } module.ackEvents[deviceIndex].clear(); module.moduleLoadEventAcked[deviceIndex] = true; return true; } DEBUG_BREAK_IF(true); return false; } void DebugSessionLinux::cleanRootSessionAfterDetach(uint32_t deviceIndex) { auto connection = clientHandleToConnection[clientHandle].get(); for (const auto &isa : connection->isaMap[deviceIndex]) { // zebin modules do not store ackEvents per ISA UNRECOVERABLE_IF(isa.second->ackEvents.size() > 0 && isa.second->perKernelModule == false); 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); } isa.second->ackEvents.clear(); isa.second->moduleLoadEventAck = true; } } ze_result_t DebugSessionLinux::acknowledgeEvent(const zet_debug_event_t *event) { const zet_debug_event_t apiEventToAck = *event; { std::unique_lock lock(asyncThreadMutex); for (size_t i = 0; i < eventsToAck.size(); i++) { if (apiEventCompare(apiEventToAck, eventsToAck[i].first)) { auto moduleUUID = eventsToAck[i].second; auto iter = eventsToAck.begin() + i; eventsToAck.erase(iter); lock.unlock(); for (uint32_t i = 0; i < NEO::EngineLimits::maxHandleCount; i++) { if (connectedDevice->getNEODevice()->getDeviceBitfield().test(i)) { ackModuleEvents(i, moduleUUID); } } return ZE_RESULT_SUCCESS; } } } if (apiEventToAck.type == ZET_DEBUG_EVENT_TYPE_MODULE_LOAD) { bool allIsaAcked = true; for (uint32_t i = 0; i < NEO::EngineLimits::maxHandleCount; i++) { if (connectedDevice->getNEODevice()->getDeviceBitfield().test(i)) { if (!ackIsaEvents(i, apiEventToAck.info.module.load)) { allIsaAcked = false; } } } if (allIsaAcked) { return ZE_RESULT_SUCCESS; } } return ZE_RESULT_ERROR_UNINITIALIZED; } ze_result_t DebugSessionLinux::readSbaBuffer(EuThread::ThreadId threadId, NEO::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::lock_guard 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::lock_guard lock(asyncThreadMutex); auto bindInfo = clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.find(memoryHandle); if (bindInfo == clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.end()) { return 0; } return bindInfo->second.gpuVa; } size_t DebugSessionLinux::getContextStateSaveAreaSize(uint64_t memoryHandle) { std::lock_guard lock(asyncThreadMutex); if (clientHandleToConnection[clientHandle]->contextStateSaveAreaSize != 0) { return clientHandleToConnection[clientHandle]->contextStateSaveAreaSize; } auto bindInfo = clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.find(memoryHandle); if (bindInfo == clientHandleToConnection[clientHandle]->vmToContextStateSaveAreaBindInfo.end()) { return 0; } clientHandleToConnection[clientHandle]->contextStateSaveAreaSize = static_cast(bindInfo->second.size); return clientHandleToConnection[clientHandle]->contextStateSaveAreaSize; } void TileDebugSessionLinux::readStateSaveAreaHeader() { const auto header = rootDebugSession->getStateSaveAreaHeader(); if (header) { auto headerSize = rootDebugSession->stateSaveAreaHeader.size(); this->stateSaveAreaHeader.assign(reinterpret_cast(header), reinterpret_cast(header) + headerSize); this->sipSupportsSlm = rootDebugSession->sipSupportsSlm; } }; bool TileDebugSessionLinux::insertModule(zet_debug_event_info_module_t module) { std::lock_guard lock(asyncThreadMutex); modules.insert({module.load, module}); return isAttached; } bool TileDebugSessionLinux::removeModule(zet_debug_event_info_module_t module) { std::lock_guard lock(asyncThreadMutex); modules.erase(module.load); return isAttached; } bool TileDebugSessionLinux::processEntry() { std::lock_guard lock(asyncThreadMutex); processEntryState = true; return isAttached; } bool TileDebugSessionLinux::processExit() { std::lock_guard lock(asyncThreadMutex); processEntryState = false; return isAttached; } void TileDebugSessionLinux::attachTile() { std::lock_guard lock(asyncThreadMutex); // clear apiEvents queue apiEvents = decltype(apiEvents){}; if (detached) { zet_debug_event_t debugEvent = {}; debugEvent.type = ZET_DEBUG_EVENT_TYPE_DETACHED; debugEvent.info.detached.reason = ZET_DEBUG_DETACH_REASON_INVALID; apiEvents.push(debugEvent); } else { if (processEntryState) { zet_debug_event_t event = {}; event.type = ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY; event.flags = 0; apiEvents.push(event); } if (modules.size()) { zet_debug_event_t event = {}; event.type = ZET_DEBUG_EVENT_TYPE_MODULE_LOAD; event.flags = 0; for (const auto &module : modules) { memcpy_s(&event.info.module, sizeof(event.info.module), &module.second, sizeof(module.second)); apiEvents.push(event); } } } isAttached = true; } void TileDebugSessionLinux::detachTile() { std::vector moduleUuids; { std::lock_guard lock(asyncThreadMutex); for (const auto &eventToAck : eventsToAck) { auto moduleUUID = eventToAck.second; moduleUuids.push_back(moduleUUID); } eventsToAck.clear(); isAttached = false; } for (const auto &uuid : moduleUuids) { rootDebugSession->ackModuleEvents(this->tileIndex, uuid); } } } // namespace L0