254 lines
7.8 KiB
C++
254 lines
7.8 KiB
C++
/*
|
|
* Copyright (C) 2018-2022 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
*/
|
|
|
|
#include "opencl/source/event/event_tracker.h"
|
|
|
|
#include "opencl/source/command_queue/command_queue.h"
|
|
#include "opencl/source/helpers/cl_helper.h"
|
|
|
|
#include <fstream>
|
|
|
|
namespace NEO {
|
|
|
|
std::unique_ptr<EventsTracker> EventsTracker::globalEvTracker = nullptr;
|
|
|
|
EventsTracker &EventsTracker::getEventsTracker() {
|
|
static std::mutex initMutex;
|
|
std::lock_guard<std::mutex> autolock(initMutex);
|
|
|
|
if (!EventsTracker::globalEvTracker) {
|
|
EventsTracker::globalEvTracker = std::unique_ptr<EventsTracker>{new EventsTracker()};
|
|
}
|
|
UNRECOVERABLE_IF(EventsTracker::globalEvTracker == nullptr);
|
|
return *EventsTracker::globalEvTracker;
|
|
}
|
|
|
|
std::string EventsTracker::label(Event *node, const EventIdMap &eventsIdMapping) {
|
|
std::string retLabel("e");
|
|
|
|
auto eventTag = eventsIdMapping.find(node);
|
|
if (eventTag != eventsIdMapping.end()) {
|
|
auto id = eventTag->second;
|
|
retLabel += std::to_string(id);
|
|
}
|
|
return retLabel;
|
|
}
|
|
|
|
std::string EventsTracker::label(CommandQueue *cmdQ) {
|
|
return "cq" + std::to_string(reinterpret_cast<uintptr_t>(cmdQ));
|
|
}
|
|
|
|
void EventsTracker::dumpQueue(CommandQueue *cmdQ, std::ostream &out, CmdqSet &dumpedCmdQs) {
|
|
if ((cmdQ == nullptr) || (dumpedCmdQs.find(cmdQ) != dumpedCmdQs.end())) {
|
|
return;
|
|
}
|
|
|
|
out << label(cmdQ) << "[label=\"{------CmdQueue, ptr=" << cmdQ << "------|task count=";
|
|
auto taskCount = cmdQ->taskCount;
|
|
auto taskLevel = cmdQ->taskLevel;
|
|
if (taskCount == CompletionStamp::notReady) {
|
|
out << "NOT_READY";
|
|
} else {
|
|
out << taskCount;
|
|
}
|
|
|
|
out << ", level=";
|
|
if (taskLevel == CompletionStamp::notReady) {
|
|
out << "NOT_READY";
|
|
} else {
|
|
out << taskLevel;
|
|
}
|
|
out << "}\",color=blue];\n";
|
|
dumpedCmdQs.insert(cmdQ);
|
|
}
|
|
|
|
void EventsTracker::dumpNode(Event *node, std::ostream &out, const EventIdMap &eventsIdMapping) {
|
|
if (node == nullptr) {
|
|
out << "eNULL[label=\"{ptr=nullptr}\",color=red];\n";
|
|
return;
|
|
}
|
|
|
|
bool isUserEvent = node->isUserEvent();
|
|
|
|
uint32_t statusId = static_cast<uint32_t>(node->peekExecutionStatus());
|
|
// clamp to aborted
|
|
statusId = (statusId > CL_QUEUED) ? (CL_QUEUED + 1) : statusId;
|
|
|
|
const char *color = ((statusId == CL_COMPLETE) || (statusId > CL_QUEUED)) ? "green" : (((statusId == CL_SUBMITTED) && (isUserEvent == false)) ? "yellow" : "red");
|
|
|
|
std::string eventType = isUserEvent ? "USER_EVENT" : (node->isCurrentCmdQVirtualEvent() ? "---V_EVENT " : "-----EVENT ");
|
|
std::string commandType = "";
|
|
if (isUserEvent == false) {
|
|
commandType = NEO::cmdTypetoString(node->getCommandType());
|
|
}
|
|
|
|
static const char *status[] = {
|
|
"CL_COMPLETE",
|
|
"CL_RUNNING",
|
|
"CL_SUBMITTED",
|
|
"CL_QUEUED",
|
|
"ABORTED"};
|
|
|
|
auto taskCount = node->peekTaskCount();
|
|
auto taskLevel = node->taskLevel.load();
|
|
|
|
out << label(node, eventsIdMapping) << "[label=\"{------" << eventType << " ptr=" << node << "------"
|
|
"|"
|
|
<< commandType << "|" << status[statusId] << "|"
|
|
"task count=";
|
|
if (taskCount == CompletionStamp::notReady) {
|
|
out << "NOT_READY";
|
|
} else {
|
|
out << taskCount;
|
|
}
|
|
|
|
out << ", level=";
|
|
if (taskLevel == CompletionStamp::notReady) {
|
|
out << "NOT_READY";
|
|
} else {
|
|
out << taskLevel;
|
|
}
|
|
|
|
out << "|CALLBACKS=" << (node->peekHasCallbacks() ? "TRUE" : "FALSE") << "}\",color=" << color << "];\n";
|
|
|
|
if (node->isCurrentCmdQVirtualEvent()) {
|
|
out << label(node->getCommandQueue()) << "->" << label(node, eventsIdMapping);
|
|
out << "[label=\"VIRTUAL_EVENT\"]";
|
|
out << ";\n";
|
|
}
|
|
}
|
|
|
|
void EventsTracker::dumpEdge(Event *leftNode, Event *rightNode, std::ostream &out, const EventIdMap &eventsIdMapping) {
|
|
out << label(leftNode, eventsIdMapping) << "->" << label(rightNode, eventsIdMapping) << ";\n";
|
|
}
|
|
|
|
// walk in DFS manner
|
|
void EventsTracker::dumpGraph(Event *node, std::ostream &out, CmdqSet &dumpedCmdQs, std::set<Event *> &dumpedEvents,
|
|
const EventIdMap &eventsIdMapping) {
|
|
if ((node == nullptr) || (dumpedEvents.find(node) != dumpedEvents.end())) {
|
|
return;
|
|
}
|
|
|
|
dumpedEvents.insert(node);
|
|
|
|
if (node->getCommandQueue() != nullptr) {
|
|
dumpQueue(node->getCommandQueue(), out, dumpedCmdQs);
|
|
}
|
|
dumpNode(node, out, eventsIdMapping);
|
|
|
|
auto *childNode = node->peekChildEvents();
|
|
while (childNode != nullptr) {
|
|
dumpGraph(childNode->ref, out, dumpedCmdQs, dumpedEvents, eventsIdMapping);
|
|
dumpEdge(node, childNode->ref, out, eventsIdMapping);
|
|
childNode = childNode->next;
|
|
}
|
|
}
|
|
|
|
TrackedEvent *EventsTracker::getNodes() {
|
|
return trackedEvents.detachNodes();
|
|
}
|
|
|
|
void EventsTracker::dump() {
|
|
static std::mutex mutex;
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
auto time = std::chrono::system_clock::now();
|
|
std::string dumpFileName = "eg_"
|
|
"reg" +
|
|
std::to_string(reinterpret_cast<uintptr_t>(this)) + "_" + std::to_string(time.time_since_epoch().count()) + ".gv";
|
|
auto out = createDumpStream(dumpFileName);
|
|
|
|
*out << "digraph events_registry_" << this << " {\n";
|
|
*out << "node [shape=record]\n";
|
|
*out << "//pragma: somePragmaData"
|
|
<< "\n";
|
|
auto allNodes = getNodes();
|
|
EventIdMap deadNodeTags;
|
|
auto curr = allNodes;
|
|
TrackedEvent *prev = nullptr;
|
|
EventIdMap eventsIdMapping;
|
|
|
|
while (curr != nullptr) {
|
|
auto next = curr->next;
|
|
bool eraseNode = false;
|
|
if (curr->eventId < 0) {
|
|
auto prevTag = deadNodeTags.find(curr->ev);
|
|
if (prevTag == deadNodeTags.end()) {
|
|
deadNodeTags[curr->ev] = -curr->eventId;
|
|
}
|
|
eraseNode = true;
|
|
} else if ((deadNodeTags.find(curr->ev) != deadNodeTags.end()) && (deadNodeTags[curr->ev] > curr->eventId)) {
|
|
eraseNode = true;
|
|
}
|
|
|
|
if (eraseNode) {
|
|
if (prev != nullptr) {
|
|
prev->next = next;
|
|
}
|
|
if (allNodes == curr) {
|
|
allNodes = nullptr;
|
|
}
|
|
delete curr;
|
|
} else {
|
|
if (allNodes == nullptr) {
|
|
allNodes = curr;
|
|
}
|
|
prev = curr;
|
|
eventsIdMapping[curr->ev] = curr->eventId;
|
|
}
|
|
curr = next;
|
|
}
|
|
|
|
auto node = allNodes;
|
|
CmdqSet dumpedCmdQs;
|
|
std::set<Event *> dumpedEvents;
|
|
while (node != nullptr) {
|
|
if (node->ev->peekNumEventsBlockingThis() != 0) {
|
|
node = node->next;
|
|
continue;
|
|
}
|
|
dumpGraph(node->ev, *out, dumpedCmdQs, dumpedEvents, eventsIdMapping);
|
|
node = node->next;
|
|
}
|
|
*out << "\n}\n";
|
|
|
|
if (allNodes == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (trackedEvents.peekHead() != nullptr) {
|
|
trackedEvents.peekHead()->getTail()->insertAllNext(*allNodes);
|
|
} else {
|
|
auto rest = allNodes->next;
|
|
trackedEvents.pushFrontOne(*allNodes);
|
|
if (rest != nullptr) {
|
|
allNodes->insertAllNext(*rest);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EventsTracker::notifyCreation(Event *eventToTrack) {
|
|
dump();
|
|
auto trackedE = new TrackedEvent{eventToTrack, eventId++};
|
|
trackedEvents.pushFrontOne(*trackedE);
|
|
}
|
|
|
|
void EventsTracker::notifyDestruction(Event *eventToDestroy) {
|
|
auto trackedE = new TrackedEvent{eventToDestroy, -(eventId++)};
|
|
trackedEvents.pushFrontOne(*trackedE);
|
|
dump();
|
|
}
|
|
|
|
void EventsTracker::notifyTransitionedExecutionStatus() {
|
|
dump();
|
|
}
|
|
|
|
std::unique_ptr<std::ostream> EventsTracker::createDumpStream(const std::string &filename) {
|
|
return std::make_unique<std::fstream>(filename, std::ios::binary | std::ios::out);
|
|
}
|
|
|
|
} // namespace NEO
|