compute-runtime/level_zero/tools/source/debug/linux/xe/debug_session.cpp

903 lines
48 KiB
C++

/*
* Copyright (C) 2023-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "level_zero/tools/source/debug/linux/xe/debug_session.h"
#include "shared/source/debug_settings/debug_settings_manager.h"
#include "shared/source/gmm_helper/gmm_helper.h"
#include "shared/source/helpers/hw_info.h"
#include "shared/source/memory_manager/memory_manager.h"
#include "shared/source/os_interface/linux/drm_debug.h"
#include "shared/source/os_interface/linux/sys_calls.h"
#include "shared/source/os_interface/linux/xe/ioctl_helper_xe.h"
#include "level_zero/core/source/gfx_core_helpers/l0_gfx_core_helper.h"
#include "level_zero/tools/source/debug/debug_session.h"
#include "level_zero/tools/source/debug/linux/drm_helper.h"
namespace L0 {
static DebugSessionLinuxPopulateFactory<DEBUG_SESSION_LINUX_TYPE_XE, DebugSessionLinuxXe>
populateXeDebugger;
DebugSession *createDebugSessionHelperXe(const zet_debug_config_t &config, Device *device, int debugFd, void *params);
DebugSessionLinuxXe::DebugSessionLinuxXe(const zet_debug_config_t &config, Device *device, int debugFd, void *params) : DebugSessionLinux(config, device, debugFd) {
auto sysFsPciPath = DrmHelper::getSysFsPciPath(device);
euDebugInterface = NEO::EuDebugInterface::create(sysFsPciPath);
if (euDebugInterface) {
ioctlHandler.reset(new IoctlHandlerXe(*euDebugInterface));
if (params) {
this->xeDebuggerVersion = reinterpret_cast<NEO::EuDebugConnect *>(params)->version;
}
}
};
DebugSessionLinuxXe::~DebugSessionLinuxXe() {
closeAsyncThread();
closeInternalEventsThread();
closeFd();
}
DebugSession *DebugSessionLinuxXe::createLinuxSession(const zet_debug_config_t &config, Device *device, ze_result_t &result, bool isRootAttach) {
NEO::EuDebugConnect open = {
.extensions = 0,
.pid = config.pid,
.flags = 0,
.version = 0};
auto debugFd = DrmHelper::ioctl(device, NEO::DrmIoctl::debuggerOpen, &open);
if (debugFd >= 0) {
PRINT_DEBUGGER_INFO_LOG("drm_xe_eudebug_connect: open.pid: %d, debugFd: %d\n",
open.pid, debugFd);
return createDebugSessionHelperXe(config, device, debugFd, &open);
} else {
auto reason = DrmHelper::getErrno(device);
PRINT_DEBUGGER_ERROR_LOG("drm_xe_eudebug_connect failed: open.pid: %d, retCode: %d, errno: %d\n",
open.pid, debugFd, reason);
result = translateDebuggerOpenErrno(reason);
}
return nullptr;
}
bool DebugSessionLinuxXe::handleInternalEvent() {
auto eventMemory = getInternalEvent();
if (eventMemory == nullptr) {
return false;
}
auto debugEvent = reinterpret_cast<NEO::EuDebugEvent *>(eventMemory.get());
handleEvent(debugEvent);
if (debugEvent->type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeExecQueuePlacements) ||
debugEvent->type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVmBindOp) ||
debugEvent->type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVmBindUfence) ||
debugEvent->type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVmBindOpMetadata)) {
processPendingVmBindEvents();
}
return true;
}
void *DebugSessionLinuxXe::asyncThreadFunction(void *arg) {
DebugSessionLinuxXe *self = reinterpret_cast<DebugSessionLinuxXe *>(arg);
if (NEO::debugManager.flags.DebugUmdFifoPollInterval.get() != -1) {
self->fifoPollInterval = NEO::debugManager.flags.DebugUmdFifoPollInterval.get();
}
if (NEO::debugManager.flags.DebugUmdInterruptTimeout.get() != -1) {
self->interruptTimeout = NEO::debugManager.flags.DebugUmdInterruptTimeout.get();
}
if (NEO::debugManager.flags.DebugUmdMaxReadWriteRetry.get() != -1) {
self->maxRetries = NEO::debugManager.flags.DebugUmdMaxReadWriteRetry.get();
}
PRINT_DEBUGGER_INFO_LOG("Debugger async thread start\n", "");
while (self->asyncThread.threadActive) {
self->handleEventsAsync();
self->pollFifo();
self->generateEventsAndResumeStoppedThreads();
self->sendInterrupts();
}
PRINT_DEBUGGER_INFO_LOG("Debugger async thread closing\n", "");
return nullptr;
}
void DebugSessionLinuxXe::startAsyncThread() {
asyncThread.thread = NEO::Thread::createFunc(asyncThreadFunction, reinterpret_cast<void *>(this));
}
void DebugSessionLinuxXe::readInternalEventsAsync() {
struct pollfd pollFd = {
.fd = fd,
.events = POLLIN,
.revents = 0,
};
int pollTimeout = 1000;
auto numberOfFds = ioctlHandler->poll(&pollFd, 1, pollTimeout);
PRINT_DEBUGGER_INFO_LOG("Debugger async thread readEvent poll() retCode: %d\n", numberOfFds);
if (!detached && numberOfFds < 0 && errno == EINVAL) {
zet_debug_event_t debugEvent = {};
debugEvent.type = ZET_DEBUG_EVENT_TYPE_DETACHED;
debugEvent.info.detached.reason = ZET_DEBUG_DETACH_REASON_INVALID;
PRINT_DEBUGGER_INFO_LOG("Debugger detached\n", "");
pushApiEvent(debugEvent);
detached = true;
} else if (numberOfFds > 0) {
ze_result_t result = ZE_RESULT_SUCCESS;
uint8_t maxEventBuffer[sizeof(NEO::EuDebugEvent) + maxEventSize];
auto event = reinterpret_cast<NEO::EuDebugEvent *>(maxEventBuffer);
event->len = maxEventSize;
event->type = euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeRead);
event->flags = 0;
result = readEventImp(event);
if (result == ZE_RESULT_SUCCESS) {
std::lock_guard<std::mutex> lock(internalEventThreadMutex);
if (eventTypeIsAttention(event->type)) {
newestAttSeqNo.store(event->seqno);
}
auto memory = std::make_unique<uint64_t[]>(maxEventSize / sizeof(uint64_t));
memcpy(memory.get(), event, maxEventSize);
internalEventQueue.push(std::move(memory));
internalEventCondition.notify_one();
}
}
}
int DebugSessionLinuxXe::ioctl(unsigned long request, void *arg) {
return ioctlHandler->ioctl(fd, request, arg);
}
ze_result_t DebugSessionLinuxXe::readEventImp(NEO::EuDebugEvent *drmDebugEvent) {
auto ret = ioctl(euDebugInterface->getParamValue(NEO::EuDebugParam::ioctlReadEvent), drmDebugEvent);
if (ret != 0) {
PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT failed: retCode: %d errno = %d\n", ret, errno);
return ZE_RESULT_NOT_READY;
} else if (drmDebugEvent->flags & ~static_cast<uint32_t>(
euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitCreate) |
euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitDestroy) |
euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitStateChange) |
euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitNeedAck))) {
PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT unsupported flag = %d\n", (int)drmDebugEvent->flags);
return ZE_RESULT_ERROR_UNKNOWN;
}
return ZE_RESULT_SUCCESS;
}
void DebugSessionLinuxXe::handleEvent(NEO::EuDebugEvent *event) {
auto type = event->type;
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type = %u flags = %u seqno = %llu len = %lu",
(uint16_t)event->type, (uint16_t)event->flags, (uint64_t)event->seqno, (uint32_t)event->len);
if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeOpen)) {
auto clientEvent = reinterpret_cast<NEO::EuDebugEventClient *>(event);
if (event->flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitCreate)) {
DEBUG_BREAK_IF(clientHandleToConnection.find(clientEvent->clientHandle) != clientHandleToConnection.end());
clientHandleToConnection[clientEvent->clientHandle].reset(new ClientConnectionXe);
clientHandleToConnection[clientEvent->clientHandle]->client = *clientEvent;
}
if (event->flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitDestroy)) {
clientHandleClosed = clientEvent->clientHandle;
}
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_OPEN client.handle = %llu\n",
(uint64_t)clientEvent->clientHandle);
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVm)) {
NEO::EuDebugEventVm *vm = reinterpret_cast<NEO::EuDebugEventVm *>(event);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_VM client_handle = %llu vm_handle = %llu\n",
(uint64_t)vm->clientHandle, (uint64_t)vm->vmHandle);
if (event->flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitCreate)) {
UNRECOVERABLE_IF(clientHandleToConnection.find(vm->clientHandle) == clientHandleToConnection.end());
clientHandleToConnection[vm->clientHandle]->vmIds.emplace(static_cast<uint64_t>(vm->vmHandle));
}
if (event->flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitDestroy)) {
UNRECOVERABLE_IF(clientHandleToConnection.find(vm->clientHandle) == clientHandleToConnection.end());
clientHandleToConnection[vm->clientHandle]->vmIds.erase(static_cast<uint64_t>(vm->vmHandle));
}
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeExecQueue)) {
NEO::EuDebugEventExecQueue *execQueue = reinterpret_cast<NEO::EuDebugEventExecQueue *>(event);
if (event->flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitCreate)) {
UNRECOVERABLE_IF(clientHandleToConnection.find(execQueue->clientHandle) == clientHandleToConnection.end());
if (!processEntryEventGenerated) {
zet_debug_event_t debugEvent = {};
debugEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY;
pushApiEvent(debugEvent);
processEntryEventGenerated = true;
}
std::lock_guard<std::mutex> lock(asyncThreadMutex);
clientHandleToConnection[execQueue->clientHandle]->execQueues[execQueue->execQueueHandle].vmHandle = execQueue->vmHandle;
clientHandleToConnection[execQueue->clientHandle]->execQueues[execQueue->execQueueHandle].engineClass = execQueue->engineClass;
for (uint16_t idx = 0; idx < execQueue->width; idx++) {
uint64_t lrcHandle = execQueue->lrcHandle[idx];
clientHandleToConnection[execQueue->clientHandle]->execQueues[execQueue->execQueueHandle].lrcHandles.push_back(lrcHandle);
clientHandleToConnection[execQueue->clientHandle]->lrcHandleToVmHandle[lrcHandle] = execQueue->vmHandle;
}
}
if (event->flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitDestroy)) {
{
std::lock_guard<std::mutex> lock(asyncThreadMutex);
for (uint16_t idx = 0; idx < execQueue->width; idx++) {
clientHandleToConnection[execQueue->clientHandle]->lrcHandleToVmHandle.erase(execQueue->lrcHandle[idx]);
}
clientHandleToConnection[execQueue->clientHandle]->execQueues.erase(execQueue->execQueueHandle);
}
{
bool lastExecQueue = true;
for (const auto &connection : clientHandleToConnection) {
if (!connection.second->execQueues.empty()) {
lastExecQueue = false;
break;
}
}
if (lastExecQueue) {
zet_debug_event_t debugEvent = {};
debugEvent.type = ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT;
pushApiEvent(debugEvent);
processEntryEventGenerated = false;
}
}
}
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE client_handle = %llu vm_handle = %llu exec_queue_handle = %llu engine_class = %u\n",
(uint64_t)execQueue->clientHandle, (uint64_t)execQueue->vmHandle,
(uint64_t)execQueue->execQueueHandle, (uint16_t)execQueue->engineClass);
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeEuAttention)) {
NEO::EuDebugEventEuAttention *attention = reinterpret_cast<NEO::EuDebugEventEuAttention *>(event);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_EU_ATTENTION client_handle = %llu flags = %llu bitmask_size = %lu exec_queue_handle = %llu lrc_handle = %llu\n",
(uint64_t)attention->clientHandle, (uint64_t)attention->flags,
(uint32_t)attention->bitmaskSize, uint64_t(attention->execQueueHandle), uint64_t(attention->lrcHandle));
if (attention->base.seqno < newestAttSeqNo.load()) {
PRINT_DEBUGGER_INFO_LOG("Dropping stale ATT event seqno=%llu\n", (uint64_t)attention->base.seqno);
} else {
handleAttentionEvent(attention);
}
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVmBind)) {
NEO::EuDebugEventVmBind *vmBind = reinterpret_cast<NEO::EuDebugEventVmBind *>(event);
UNRECOVERABLE_IF(clientHandleToConnection.find(vmBind->clientHandle) == clientHandleToConnection.end());
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_VM_BIND client_handle = %llu vm_handle = %llu num_binds = %llu vmBindflag=%lu\n",
static_cast<uint64_t>(vmBind->clientHandle), static_cast<uint64_t>(vmBind->vmHandle),
static_cast<uint64_t>(vmBind->numBinds), static_cast<uint32_t>(vmBind->flags));
auto &connection = clientHandleToConnection[vmBind->clientHandle];
UNRECOVERABLE_IF(connection->vmBindMap.find(vmBind->base.seqno) != connection->vmBindMap.end());
auto &vmBindData = connection->vmBindMap[vmBind->base.seqno];
vmBindData.vmBind = *vmBind;
vmBindData.pendingNumBinds = vmBind->numBinds;
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVmBindOp)) {
NEO::EuDebugEventVmBindOp *vmBindOp = reinterpret_cast<NEO::EuDebugEventVmBindOp *>(event);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: drm_xe_eudebug_event_vm_bind_op vm_bind_ref_seqno = %llu num_extensions = %llu addr = 0x%llx range = %llu\n",
static_cast<uint64_t>(vmBindOp->vmBindRefSeqno), static_cast<uint64_t>(vmBindOp->numExtensions),
static_cast<uint64_t>(vmBindOp->addr), static_cast<uint64_t>(vmBindOp->range));
auto &vmBindMap = clientHandleToConnection[clientHandle]->vmBindMap;
UNRECOVERABLE_IF(vmBindMap.find(vmBindOp->vmBindRefSeqno) == vmBindMap.end());
auto &vmBindData = vmBindMap[vmBindOp->vmBindRefSeqno];
UNRECOVERABLE_IF(!vmBindData.pendingNumBinds);
auto &vmBindOpData = vmBindData.vmBindOpMap[vmBindOp->base.seqno];
vmBindOpData.pendingNumExtensions = vmBindOp->numExtensions;
vmBindOpData.vmBindOp = *vmBindOp;
vmBindData.pendingNumBinds--;
clientHandleToConnection[clientHandle]->vmBindIdentifierMap[vmBindOp->base.seqno] = vmBindOp->vmBindRefSeqno;
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVmBindUfence)) {
NEO::EuDebugEventVmBindUfence *vmBindUfence = reinterpret_cast<NEO::EuDebugEventVmBindUfence *>(event);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE vm_bind_ref_seqno = %llu\n",
static_cast<uint64_t>(vmBindUfence->vmBindRefSeqno));
auto &vmBindMap = clientHandleToConnection[clientHandle]->vmBindMap;
UNRECOVERABLE_IF(vmBindMap.find(vmBindUfence->vmBindRefSeqno) == vmBindMap.end());
uint32_t uFenceRequired = vmBindMap[vmBindUfence->vmBindRefSeqno].vmBind.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventVmBindFlagUfence);
UNRECOVERABLE_IF(!uFenceRequired);
UNRECOVERABLE_IF(vmBindMap[vmBindUfence->vmBindRefSeqno].uFenceReceived); // Dont expect multiple UFENCE for same vm_bind
vmBindMap[vmBindUfence->vmBindRefSeqno].uFenceReceived = true;
vmBindMap[vmBindUfence->vmBindRefSeqno].vmBindUfence = *vmBindUfence;
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeMetadata)) {
NEO::EuDebugEventMetadata *metaData = reinterpret_cast<NEO::EuDebugEventMetadata *>(event);
if (clientHandle == invalidClientHandle) {
clientHandle = metaData->clientHandle;
}
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_METADATA client_handle = %llu metadata_handle = %llu type = %llu len = %llu\n",
(uint64_t)metaData->clientHandle, (uint64_t)metaData->metadataHandle, (uint64_t)metaData->type, (uint64_t)metaData->len);
handleMetadataEvent(metaData);
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeVmBindOpMetadata)) {
NEO::EuDebugEventVmBindOpMetadata *vmBindOpMetadata = reinterpret_cast<NEO::EuDebugEventVmBindOpMetadata *>(event);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_METADATA vm_bind_op_ref_seqno = %llu metadata_handle = %llu metadata_cookie = %llu\n",
static_cast<uint64_t>(vmBindOpMetadata->vmBindOpRefSeqno), static_cast<uint64_t>(vmBindOpMetadata->metadataHandle),
static_cast<uint64_t>(vmBindOpMetadata->metadataCookie));
auto &vmBindMap = clientHandleToConnection[clientHandle]->vmBindMap;
auto &vmBindIdentifierMap = clientHandleToConnection[clientHandle]->vmBindIdentifierMap;
UNRECOVERABLE_IF(vmBindIdentifierMap.find(vmBindOpMetadata->vmBindOpRefSeqno) == vmBindIdentifierMap.end());
VmBindSeqNo vmBindSeqNo = vmBindIdentifierMap[vmBindOpMetadata->vmBindOpRefSeqno];
UNRECOVERABLE_IF(vmBindMap.find(vmBindSeqNo) == vmBindMap.end());
auto &vmBindOpData = vmBindMap[vmBindSeqNo].vmBindOpMap[vmBindOpMetadata->vmBindOpRefSeqno];
UNRECOVERABLE_IF(!vmBindOpData.pendingNumExtensions);
vmBindOpData.vmBindOpMetadataVec.push_back(*vmBindOpMetadata);
vmBindOpData.pendingNumExtensions--;
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypeExecQueuePlacements)) {
NEO::EuDebugEventExecQueuePlacements *execQueuePlacements = reinterpret_cast<NEO::EuDebugEventExecQueuePlacements *>(event);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: PRELIM_DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE_PLACEMENTS client_handle = %" SCNx64
" vm_handle = %" SCNx64
" exec_queue_handle = %" SCNx64
" lrc_handle = %" SCNx64
" num_placements = %" SCNx32
"\n ",
static_cast<uint64_t>(execQueuePlacements->clientHandle),
static_cast<uint64_t>(execQueuePlacements->vmHandle),
static_cast<uint64_t>(execQueuePlacements->execQueueHandle), static_cast<uint64_t>(execQueuePlacements->lrcHandle),
static_cast<uint32_t>(execQueuePlacements->numPlacements));
UNRECOVERABLE_IF(execQueuePlacements->numPlacements == 0);
auto engine = reinterpret_cast<NEO::XeEngineClassInstance *>(&(execQueuePlacements->instances[0]));
auto tileIndex = engine->gtId;
auto &vmToTile = clientHandleToConnection[execQueuePlacements->clientHandle]->vmToTile;
if (vmToTile.find(execQueuePlacements->vmHandle) != vmToTile.end()) {
if (vmToTile[execQueuePlacements->vmHandle] != tileIndex) {
PRINT_DEBUGGER_ERROR_LOG("vmToTile map: For vm_handle = %lu tileIndex = %u already present. Attempt to overwrite with tileIndex = %u\n",
static_cast<uint64_t>(execQueuePlacements->vmHandle), vmToTile[execQueuePlacements->vmHandle], tileIndex);
DEBUG_BREAK_IF(true);
}
} else {
clientHandleToConnection[execQueuePlacements->clientHandle]->vmToTile[execQueuePlacements->vmHandle] = tileIndex;
PRINT_DEBUGGER_INFO_LOG("clientHandleToConnection[%" SCNx64 "]->vmToTile[%" SCNx64 "] = %u\n",
static_cast<uint64_t>(execQueuePlacements->clientHandle), static_cast<uint64_t>(execQueuePlacements->vmHandle), tileIndex);
}
} else if (type == euDebugInterface->getParamValue(NEO::EuDebugParam::eventTypePagefault)) {
NEO::EuDebugEventPageFault *pf = reinterpret_cast<NEO::EuDebugEventPageFault *>(event);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT type: DRM_XE_EUDEBUG_EVENT_PAGEFAULT flags = %d, address = %llu seqno = %d, length = %llu"
" client_handle = %llu pf_flags = %llu bitmask_size = %lu exec_queue_handle = %llu\n",
(int)pf->base.flags, (uint64_t)pf->pagefaultAddress, (uint64_t)pf->base.seqno, (uint64_t)pf->base.len,
(uint64_t)pf->clientHandle, (uint64_t)pf->flags, (uint32_t)pf->bitmaskSize, uint64_t(pf->execQueueHandle));
auto tileIndex = 0u;
auto vmHandle = getVmHandleFromClientAndlrcHandle(pf->clientHandle, pf->lrcHandle);
if (vmHandle == invalidHandle) {
return;
}
PageFaultEvent pfEvent = {vmHandle, tileIndex, pf->pagefaultAddress, pf->bitmaskSize, pf->bitmask};
handlePageFaultEvent(pfEvent);
} else {
additionalEvents(event);
}
}
void DebugSessionLinuxXe::processPendingVmBindEvents() {
if (clientHandle == invalidClientHandle) {
return;
}
std::vector<decltype(clientHandleToConnection[clientHandle]->vmBindMap)::key_type> keysToDelete;
auto &vmBindMap = clientHandleToConnection[clientHandle]->vmBindMap;
for (auto &pair : vmBindMap) {
auto &vmBindData = pair.second;
if (handleVmBind(vmBindData) == false) {
break;
}
keysToDelete.push_back(pair.first);
}
for (const auto &key : keysToDelete) {
vmBindMap.erase(key);
}
}
bool DebugSessionLinuxXe::canHandleVmBind(VmBindData &vmBindData) const {
if (vmBindData.pendingNumBinds) {
return false;
}
for (const auto &vmBindOpData : vmBindData.vmBindOpMap) {
if (vmBindOpData.second.pendingNumExtensions) {
return false;
}
}
uint32_t uFenceRequired = vmBindData.vmBind.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventVmBindFlagUfence);
if (uFenceRequired) {
if (!vmBindData.uFenceReceived) {
return false;
}
}
return true;
}
bool DebugSessionLinuxXe::handleVmBind(VmBindData &vmBindData) {
if (!canHandleVmBind(vmBindData)) {
return false;
}
bool shouldAckEvent = vmBindData.vmBind.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventVmBindFlagUfence);
auto connection = clientHandleToConnection[clientHandle].get();
auto elfHandleInVmBind = invalidHandle;
uint64_t moduleHandle = invalidHandle;
uint64_t isaAddr = 0;
bool triggerModuleLoadEvent = false;
uint32_t tileIndex = 0;
auto numTiles = connectedDevice->getNEODevice()->getNumSubDevices();
if (numTiles > 0) {
if (connection->vmToTile.find(vmBindData.vmBind.vmHandle) != connection->vmToTile.end()) {
tileIndex = connection->vmToTile[vmBindData.vmBind.vmHandle];
PRINT_DEBUGGER_INFO_LOG("handleVmBind: tileIndex = %d for vmHandle = %" SCNx64 " \n", tileIndex, vmBindData.vmBind.vmHandle);
} else {
PRINT_DEBUGGER_ERROR_LOG("handleVmBind: tileIndex not found for vmHandle = %" SCNx64 " \n", vmBindData.vmBind.vmHandle);
return false;
}
}
for (auto &vmBindOpData : vmBindData.vmBindOpMap) {
auto &vmBindOp = vmBindOpData.second.vmBindOp;
for (const auto &vmBindOpMetadata : vmBindOpData.second.vmBindOpMetadataVec) {
const NEO::DeviceBitfield devices(vmBindOpMetadata.metadataCookie);
auto &metaDataEntry = connection->metaDataMap[vmBindOpMetadata.metadataHandle];
if (vmBindOp.base.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitCreate)) {
{
std::lock_guard<std::mutex> lock(asyncThreadMutex);
if (metaDataEntry.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataSbaArea)) {
connection->vmToStateBaseAreaBindInfo[vmBindData.vmBind.vmHandle] = {vmBindOp.addr, vmBindOp.range};
}
if (metaDataEntry.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataSipArea)) {
connection->vmToContextStateSaveAreaBindInfo[vmBindData.vmBind.vmHandle] = {vmBindOp.addr, vmBindOp.range};
}
if (metaDataEntry.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataModuleArea)) {
isaAddr = vmBindOp.addr;
if (connection->isaMap[tileIndex].find(vmBindOp.addr) == connection->isaMap[tileIndex].end()) {
auto &isaMap = connection->isaMap[tileIndex];
auto isa = std::make_unique<IsaAllocation>();
isa->bindInfo = {vmBindOp.addr, vmBindOp.range};
isa->vmHandle = vmBindData.vmBind.vmHandle;
isa->elfHandle = invalidHandle;
isa->moduleBegin = 0;
isa->moduleEnd = 0;
isa->tileInstanced = !(this->debugArea.isShared);
isa->validVMs.insert(vmBindData.vmBind.vmHandle);
uint32_t deviceBitfield = 0;
auto &debugModule = connection->metaDataToModule[vmBindOpMetadata.metadataHandle];
memcpy_s(&deviceBitfield, sizeof(uint32_t),
&debugModule.deviceBitfield,
sizeof(debugModule.deviceBitfield));
const NEO::DeviceBitfield devices(deviceBitfield);
isa->deviceBitfield = devices;
isa->perKernelModule = false;
isaMap[vmBindOp.addr] = std::move(isa);
}
connection->vmToModuleDebugAreaBindInfo[vmBindData.vmBind.vmHandle] = {vmBindOp.addr, vmBindOp.range};
}
}
if (metaDataEntry.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataElfBinary)) {
isaAddr = vmBindOp.addr;
if (connection->isaMap[tileIndex].find(vmBindOp.addr) == connection->isaMap[tileIndex].end()) {
auto &isaMap = connection->isaMap[tileIndex];
auto &elfMap = connection->elfMap;
auto isa = std::make_unique<IsaAllocation>();
isa->bindInfo = {vmBindOp.addr, vmBindOp.range};
isa->vmHandle = vmBindData.vmBind.vmHandle;
isa->elfHandle = vmBindOpMetadata.metadataHandle;
isa->moduleBegin = reinterpret_cast<uint64_t>(metaDataEntry.data.get());
isa->moduleEnd = isa->moduleBegin + metaDataEntry.metadata.len;
isa->tileInstanced = (devices.count() != 0);
isa->validVMs.insert(vmBindData.vmBind.vmHandle);
isa->perKernelModule = false;
isa->deviceBitfield = devices;
elfMap[isa->moduleBegin] = isa->elfHandle;
isaMap[vmBindOp.addr] = std::move(isa);
isaMap[vmBindOp.addr]->moduleLoadEventAck = true;
elfHandleInVmBind = vmBindOpMetadata.metadataHandle;
} else {
auto &isa = connection->isaMap[tileIndex][vmBindOp.addr];
isa->validVMs.insert(vmBindData.vmBind.vmHandle);
}
}
if (metaDataEntry.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataProgramModule)) {
auto &module = connection->metaDataToModule[vmBindOpMetadata.metadataHandle];
module.deviceBitfield = devices;
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(vmBindOp.addr);
moduleHandle = vmBindOpMetadata.metadataHandle;
if (canTriggerEvent && module.loadAddresses[tileIndex].size() == module.segmentCount) {
bool allInstancesEventsReceived = true;
if (module.deviceBitfield.count() > 1) {
allInstancesEventsReceived = checkAllOtherTileModuleSegmentsPresent(tileIndex, module);
}
triggerModuleLoadEvent = allInstancesEventsReceived;
}
}
}
}
if (vmBindOp.base.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitDestroy)) {
if (connection->isaMap[tileIndex].count(vmBindOp.addr)) {
auto &isa = connection->isaMap[tileIndex][vmBindOp.addr];
if (isa->validVMs.count(vmBindData.vmBind.vmHandle)) {
auto &module = connection->metaDataToModule[isa->moduleHandle];
module.segmentVmBindCounter[tileIndex]--;
if (module.segmentVmBindCounter[tileIndex] == 0) {
zet_debug_event_t debugEvent = {};
auto &metaDataEntry = connection->metaDataMap[isa->moduleHandle];
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;
auto &elfMetadata = connection->metaDataMap[isa->elfHandle];
debugEvent.info.module.moduleBegin = reinterpret_cast<uint64_t>(elfMetadata.data.get());
debugEvent.info.module.moduleEnd = reinterpret_cast<uint64_t>(elfMetadata.data.get()) + elfMetadata.metadata.len;
bool notifyEvent = true;
if (module.deviceBitfield.count() > 1) {
notifyEvent = checkAllOtherTileModuleSegmentsRemoved(tileIndex, module);
}
if (notifyEvent) {
pushApiEvent(debugEvent, metaDataEntry.metadata.metadataHandle);
}
module.loadAddresses[tileIndex].clear();
module.moduleLoadEventAcked[tileIndex] = false;
}
isa->validVMs.erase(vmBindData.vmBind.vmHandle);
}
}
}
}
if (isaAddr && moduleHandle != invalidHandle) {
connection->isaMap[tileIndex][isaAddr]->moduleHandle = moduleHandle;
}
if (triggerModuleLoadEvent) {
DEBUG_BREAK_IF(moduleHandle == invalidHandle);
DEBUG_BREAK_IF(elfHandleInVmBind == invalidHandle);
auto &metaDataEntry = connection->metaDataMap[moduleHandle];
auto &module = connection->metaDataToModule[moduleHandle];
auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper();
auto 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);
auto &elfMetadata = connection->metaDataMap[elfHandleInVmBind];
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 = reinterpret_cast<uint64_t>(elfMetadata.data.get());
debugEvent.info.module.moduleEnd = reinterpret_cast<uint64_t>(elfMetadata.data.get()) + elfMetadata.metadata.len;
debugEvent.flags = ZET_DEBUG_EVENT_FLAG_NEED_ACK;
{
std::lock_guard<std::mutex> lock(asyncThreadMutex);
if (vmBindData.vmBind.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventVmBindFlagUfence)) {
if (vmBindData.vmBindUfence.base.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitNeedAck)) {
EventToAck ackEvent(vmBindData.vmBindUfence.base.seqno, vmBindData.vmBindUfence.base.type);
module.ackEvents[tileIndex].push_back(ackEvent);
shouldAckEvent = false;
}
}
}
pushApiEvent(debugEvent, metaDataEntry.metadata.metadataHandle);
}
if (shouldAckEvent && (vmBindData.vmBindUfence.base.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitNeedAck))) {
EventToAck ackEvent(vmBindData.vmBindUfence.base.seqno, vmBindData.vmBindUfence.base.type);
eventAckIoctl(ackEvent);
}
return true;
}
void DebugSessionLinuxXe::handleMetadataEvent(NEO::EuDebugEventMetadata *metaData) {
bool destroy = metaData->base.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitDestroy);
bool create = metaData->base.flags & euDebugInterface->getParamValue(NEO::EuDebugParam::eventBitCreate);
UNRECOVERABLE_IF(clientHandleToConnection.find(metaData->clientHandle) == clientHandleToConnection.end());
const auto &connection = clientHandleToConnection[metaData->clientHandle];
if (destroy && connection->metaDataMap[metaData->metadataHandle].metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataProgramModule)) {
DEBUG_BREAK_IF(connection->metaDataToModule[metaData->metadataHandle].segmentVmBindCounter[0] != 0 ||
connection->metaDataToModule[metaData->metadataHandle].loadAddresses[0].size() > 0);
connection->metaDataToModule.erase(metaData->metadataHandle);
}
if (create) {
if (!metaData->len) {
connection->metaDataMap[metaData->metadataHandle].metadata = *metaData;
return;
}
NEO::EuDebugReadMetadata readMetadata{};
auto ptr = std::make_unique<char[]>(metaData->len);
readMetadata.clientHandle = metaData->clientHandle;
readMetadata.metadataHandle = static_cast<decltype(readMetadata.metadataHandle)>(metaData->metadataHandle);
readMetadata.ptr = reinterpret_cast<uint64_t>(ptr.get());
readMetadata.size = metaData->len;
auto ret = ioctl(euDebugInterface->getParamValue(NEO::EuDebugParam::ioctlReadMetadata), &readMetadata);
if (ret != 0) {
PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_READ_METADATA ret = %d errno = %d\n", ret, errno);
return;
}
connection->metaDataMap[metaData->metadataHandle].metadata = *metaData;
connection->metaDataMap[metaData->metadataHandle].data = std::move(ptr);
if (metaData->type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataProgramModule)) {
auto handle = metaData->metadataHandle;
auto &newModule = connection->metaDataToModule[handle];
newModule.segmentCount = 0;
newModule.moduleHandle = handle;
for (uint32_t i = 0; i < NEO::EngineLimits::maxHandleCount; i++) {
newModule.segmentVmBindCounter[i] = 0;
newModule.loadAddresses[i].clear();
newModule.moduleLoadEventAcked[i] = false;
}
}
extractMetaData(metaData->clientHandle, connection->metaDataMap[metaData->metadataHandle]);
}
}
void DebugSessionLinuxXe::extractMetaData(uint64_t client, const MetaData &metaData) {
if (metaData.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataSbaArea) ||
metaData.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataModuleArea) ||
metaData.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataSipArea)) {
UNRECOVERABLE_IF(metaData.metadata.len != 8);
uint64_t *data = (uint64_t *)metaData.data.get();
if (metaData.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataSbaArea)) {
clientHandleToConnection[client]->stateBaseAreaGpuVa = *data;
PRINT_DEBUGGER_INFO_LOG("SbaTrackingBuffer GPU VA = %p", (void *)clientHandleToConnection[clientHandle]->stateBaseAreaGpuVa);
}
if (metaData.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataModuleArea)) {
clientHandleToConnection[client]->moduleDebugAreaGpuVa = *data;
PRINT_DEBUGGER_INFO_LOG("ModuleHeapDebugArea GPU VA = %p", (void *)clientHandleToConnection[clientHandle]->moduleDebugAreaGpuVa);
}
if (metaData.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataSipArea)) {
clientHandleToConnection[client]->contextStateSaveAreaGpuVa = *data;
PRINT_DEBUGGER_INFO_LOG("ContextSaveArea GPU VA = %p", (void *)clientHandleToConnection[clientHandle]->contextStateSaveAreaGpuVa);
}
}
if (metaData.metadata.type == euDebugInterface->getParamValue(NEO::EuDebugParam::metadataProgramModule)) {
uint32_t segmentCount = 0;
memcpy_s(&segmentCount, sizeof(uint32_t), metaData.data.get(), metaData.metadata.len);
clientHandleToConnection[client]->metaDataToModule[metaData.metadata.metadataHandle].segmentCount = segmentCount;
PRINT_DEBUGGER_INFO_LOG("Zebin module = %ull, segment count = %ul", metaData.metadata.metadataHandle, segmentCount);
}
}
int DebugSessionLinuxXe::openVmFd(uint64_t vmHandle, [[maybe_unused]] bool readOnly) {
NEO::EuDebugVmOpen vmOpen = {
.extensions = 0,
.clientHandle = clientHandle,
.vmHandle = vmHandle,
.flags = 0,
.timeoutNs = 5000000000u};
return ioctl(euDebugInterface->getParamValue(NEO::EuDebugParam::ioctlVmOpen), &vmOpen);
}
int DebugSessionLinuxXe::flushVmCache(int vmfd) {
int retVal = ioctlHandler->fsync(vmfd);
if (retVal != 0) {
PRINT_DEBUGGER_ERROR_LOG("Failed to fsync VM fd=%d errno=%d\n", vmfd, errno);
}
return retVal;
}
uint64_t DebugSessionLinuxXe::getVmHandleFromClientAndlrcHandle(uint64_t clientHandle, uint64_t lrcHandle) {
if (clientHandleToConnection.find(clientHandle) == clientHandleToConnection.end()) {
return invalidHandle;
}
auto &clientConnection = clientHandleToConnection[clientHandle];
if (clientConnection->lrcHandleToVmHandle.find(lrcHandle) == clientConnection->lrcHandleToVmHandle.end()) {
return invalidHandle;
}
return clientConnection->lrcHandleToVmHandle[lrcHandle];
}
void DebugSessionLinuxXe::handleAttentionEvent(NEO::EuDebugEventEuAttention *attention) {
if (interruptSent) {
if (attention->base.seqno <= euControlInterruptSeqno) {
PRINT_DEBUGGER_INFO_LOG("Discarding EU ATTENTION event for interrupt request. Event seqno == %llu <= %llu == interrupt seqno\n",
static_cast<uint64_t>(attention->base.seqno), euControlInterruptSeqno);
return;
}
}
newAttentionRaised();
std::vector<EuThread::ThreadId> threadsWithAttention;
AttentionEventFields attentionEventFields;
attentionEventFields.bitmask = attention->bitmask;
attentionEventFields.bitmaskSize = attention->bitmaskSize;
attentionEventFields.clientHandle = attention->clientHandle;
attentionEventFields.contextHandle = attention->execQueueHandle;
attentionEventFields.lrcHandle = attention->lrcHandle;
return updateStoppedThreadsAndCheckTriggerEvents(attentionEventFields, 0, threadsWithAttention);
}
int DebugSessionLinuxXe::threadControlInterruptAll() {
int euControlRetVal = -1;
NEO::EuDebugEuControl euControl = {};
euControl.clientHandle = clientHandle;
euControl.cmd = euDebugInterface->getParamValue(NEO::EuDebugParam::euControlCmdInterruptAll);
euControl.bitmaskSize = 0;
euControl.bitmaskPtr = 0;
DEBUG_BREAK_IF(clientHandleToConnection.find(clientHandle) == clientHandleToConnection.end());
std::lock_guard<std::mutex> lock(asyncThreadMutex);
for (const auto &execQueue : clientHandleToConnection[clientHandle]->execQueues) {
euControl.execQueueHandle = execQueue.first;
for (const auto &lrcHandle : execQueue.second.lrcHandles) {
euControl.lrcHandle = lrcHandle;
euControlRetVal = ioctl(euDebugInterface->getParamValue(NEO::EuDebugParam::ioctlEuControl), &euControl);
if (euControlRetVal != 0) {
PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_EU_CONTROL failed: retCode: %d errno = %d command = %d, execQueueHandle = %llu lrcHandle = %llu\n",
euControlRetVal, errno, static_cast<uint32_t>(euControl.cmd), static_cast<uint64_t>(euControl.execQueueHandle),
static_cast<uint64_t>(euControl.lrcHandle));
} else {
DEBUG_BREAK_IF(euControlInterruptSeqno >= euControl.seqno);
euControlInterruptSeqno = euControl.seqno;
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_EU_CONTROL: seqno = %llu command = %u\n", static_cast<uint64_t>(euControl.seqno),
static_cast<uint32_t>(euControl.cmd));
}
}
}
return euControlRetVal;
}
int DebugSessionLinuxXe::threadControlStopped(std::unique_ptr<uint8_t[]> &bitmaskOut, size_t &bitmaskSizeOut) {
int euControlRetVal = -1;
auto hwInfo = connectedDevice->getHwInfo();
auto &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper();
NEO::EuDebugEuControl euControl = {};
euControl.clientHandle = clientHandle;
euControl.cmd = euDebugInterface->getParamValue(NEO::EuDebugParam::euControlCmdStopped);
std::unique_ptr<uint8_t[]> bitmask;
size_t bitmaskSize = 0;
l0GfxCoreHelper.getAttentionBitmaskForSingleThreads({}, hwInfo, bitmask, bitmaskSize);
euControl.bitmaskSize = static_cast<uint32_t>(bitmaskSize);
euControl.bitmaskPtr = reinterpret_cast<uint64_t>(bitmask.get());
DEBUG_BREAK_IF(clientHandleToConnection.find(clientHandle) == clientHandleToConnection.end());
std::lock_guard<std::mutex> lock(asyncThreadMutex);
for (const auto &execQueue : clientHandleToConnection[clientHandle]->execQueues) {
euControl.execQueueHandle = execQueue.first;
for (const auto &lrcHandle : execQueue.second.lrcHandles) {
euControl.lrcHandle = lrcHandle;
euControlRetVal = ioctl(euDebugInterface->getParamValue(NEO::EuDebugParam::ioctlEuControl), &euControl);
if (euControlRetVal != 0) {
PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_EU_CONTROL failed: retCode: %d errno = %d command = %d, execQueueHandle = %llu lrcHandle = %llu\n",
euControlRetVal, errno, static_cast<uint32_t>(euControl.cmd), static_cast<uint64_t>(euControl.execQueueHandle),
static_cast<uint64_t>(euControl.lrcHandle));
} else {
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_EU_CONTROL: seqno = %llu command = %u\n", static_cast<uint64_t>(euControl.seqno),
static_cast<uint32_t>(euControl.cmd));
break;
}
}
if (euControlRetVal == 0) {
break;
}
}
printBitmask(bitmask.get(), bitmaskSize);
bitmaskOut = std::move(bitmask);
UNRECOVERABLE_IF(bitmaskOut.get() == nullptr);
bitmaskSizeOut = euControl.bitmaskSize;
return euControlRetVal;
}
int DebugSessionLinuxXe::threadControlResume(const std::vector<EuThread::ThreadId> &threads) {
int euControlRetVal = -1;
auto hwInfo = connectedDevice->getHwInfo();
auto &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper();
NEO::EuDebugEuControl euControl = {};
euControl.clientHandle = clientHandle;
euControl.bitmaskSize = 0;
euControl.bitmaskPtr = 0;
std::unique_ptr<uint8_t[]> bitmask;
size_t bitmaskSize = 0;
l0GfxCoreHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize);
euControl.bitmaskSize = static_cast<uint32_t>(bitmaskSize);
euControl.bitmaskPtr = reinterpret_cast<uint64_t>(bitmask.get());
printBitmask(bitmask.get(), bitmaskSize);
uint64_t execQueueHandle{0};
uint64_t lrcHandle{0};
allThreads[threads[0]]->getContextHandle(execQueueHandle);
allThreads[threads[0]]->getLrcHandle(lrcHandle);
euControl.execQueueHandle = execQueueHandle;
euControl.lrcHandle = lrcHandle;
auto invokeIoctl = [&](int cmd) {
euControl.cmd = cmd;
euControlRetVal = ioctl(euDebugInterface->getParamValue(NEO::EuDebugParam::ioctlEuControl), &euControl);
if (euControlRetVal != 0) {
PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_EU_CONTROL failed: retCode: %d errno = %d command = %d, execQueueHandle = %llu lrcHandle = %llu\n",
euControlRetVal, errno, static_cast<uint32_t>(euControl.cmd), static_cast<uint64_t>(euControl.execQueueHandle),
static_cast<uint64_t>(euControl.lrcHandle));
} else {
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_EU_CONTROL: seqno = %llu command = %u\n", static_cast<uint64_t>(euControl.seqno), static_cast<uint32_t>(euControl.cmd));
}
};
if (l0GfxCoreHelper.threadResumeRequiresUnlock()) {
invokeIoctl(getEuControlCmdUnlock());
}
invokeIoctl(euDebugInterface->getParamValue(NEO::EuDebugParam::euControlCmdResume));
return euControlRetVal;
}
int DebugSessionLinuxXe::threadControl(const std::vector<EuThread::ThreadId> &threads, uint32_t tile,
ThreadControlCmd threadCmd, std::unique_ptr<uint8_t[]> &bitmaskOut, size_t &bitmaskSizeOut) {
bitmaskSizeOut = 0;
switch (threadCmd) {
case ThreadControlCmd::interruptAll:
return threadControlInterruptAll();
case ThreadControlCmd::resume:
return threadControlResume(threads);
case ThreadControlCmd::stopped:
return threadControlStopped(bitmaskOut, bitmaskSizeOut);
default:
break;
}
return -1;
}
void DebugSessionLinuxXe::updateContextAndLrcHandlesForThreadsWithAttention(EuThread::ThreadId threadId, const AttentionEventFields &attention) {
allThreads[threadId]->setContextHandle(attention.contextHandle);
allThreads[threadId]->setLrcHandle(attention.lrcHandle);
}
int DebugSessionLinuxXe::eventAckIoctl(EventToAck &event) {
NEO::EuDebugAckEvent eventToAck = {};
eventToAck.type = event.type;
eventToAck.seqno = event.seqno;
eventToAck.flags = 0;
auto ret = ioctl(euDebugInterface->getParamValue(NEO::EuDebugParam::ioctlAckEvent), &eventToAck);
PRINT_DEBUGGER_INFO_LOG("DRM_XE_EUDEBUG_IOCTL_ACK_EVENT seqno = %llu ret = %d errno = %d\n", static_cast<uint64_t>(eventToAck.seqno), ret, ret != 0 ? errno : 0);
return ret;
}
} // namespace L0