From 56125ea381a825a224a238cf6af44d087c029a89 Mon Sep 17 00:00:00 2001 From: "Jobczyk, Lukasz" Date: Tue, 29 May 2018 13:30:39 +0200 Subject: [PATCH] Add tool for tracking events Change-Id: Id61d814e4629a41a279d46097ec8b4f94a224234 --- runtime/event/CMakeLists.txt | 2 + runtime/event/event.cpp | 20 + runtime/event/event.h | 9 +- runtime/event/event_tracker.cpp | 274 ++++++++++ runtime/event/event_tracker.h | 68 +++ runtime/helpers/CMakeLists.txt | 1 + runtime/helpers/cl_helper.h | 97 ++++ runtime/os_interface/DebugVariables.inl | 1 + unit_tests/event/CMakeLists.txt | 1 + unit_tests/event/event_tracker_tests.cpp | 667 +++++++++++++++++++++++ unit_tests/helpers/CMakeLists.txt | 1 + unit_tests/helpers/cl_helper_tests.cpp | 71 +++ unit_tests/test_files/igdrcl.config | 1 + 13 files changed, 1205 insertions(+), 8 deletions(-) create mode 100644 runtime/event/event_tracker.cpp create mode 100644 runtime/event/event_tracker.h create mode 100644 runtime/helpers/cl_helper.h create mode 100644 unit_tests/event/event_tracker_tests.cpp create mode 100644 unit_tests/helpers/cl_helper_tests.cpp diff --git a/runtime/event/CMakeLists.txt b/runtime/event/CMakeLists.txt index a5538ef1de..e488a3d885 100644 --- a/runtime/event/CMakeLists.txt +++ b/runtime/event/CMakeLists.txt @@ -26,6 +26,8 @@ set(RUNTIME_SRCS_EVENT ${CMAKE_CURRENT_SOURCE_DIR}/event.h ${CMAKE_CURRENT_SOURCE_DIR}/event_builder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/event_builder.h + ${CMAKE_CURRENT_SOURCE_DIR}/event_tracker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/event_tracker.h ${CMAKE_CURRENT_SOURCE_DIR}/user_event.cpp ${CMAKE_CURRENT_SOURCE_DIR}/user_event.h ${CMAKE_CURRENT_SOURCE_DIR}/hw_timestamps.h diff --git a/runtime/event/event.cpp b/runtime/event/event.cpp index 3b7c77d782..6a7ec74466 100644 --- a/runtime/event/event.cpp +++ b/runtime/event/event.cpp @@ -27,6 +27,7 @@ #include "runtime/context/context.h" #include "runtime/device/device.h" #include "runtime/event/event.h" +#include "runtime/event/event_tracker.h" #include "runtime/helpers/aligned_memory.h" #include "runtime/helpers/get_info.h" #include "runtime/api/cl_types.h" @@ -59,6 +60,9 @@ Event::Event( perfCounterNode(nullptr), perfConfigurationData(nullptr), taskCount(taskCount) { + if (OCLRT::DebugManager.flags.EventsTrackerEnable.get()) { + EventsTracker::getEventsTracker().notifyCreation(this); + } parentCount = 0; executionStatus = CL_QUEUED; flushStamp.reset(new FlushStampTracker(true)); @@ -103,6 +107,10 @@ Event::Event( } Event::~Event() { + if (OCLRT::DebugManager.flags.EventsTrackerEnable.get()) { + EventsTracker::getEventsTracker().notifyDestruction(this); + } + DBG_LOG(EventsDebugEnable, "~Event()", this); //no commands should be registred DEBUG_BREAK_IF(this->cmdToSubmit.load()); @@ -425,6 +433,18 @@ bool Event::setStatus(cl_int status) { return true; } +void Event::transitionExecutionStatus(int32_t newExecutionStatus) const { + int32_t prevStatus = executionStatus; + DBG_LOG(EventsDebugEnable, "transitionExecutionStatus event", this, " new status", newExecutionStatus, "previousStatus", prevStatus); + + while (prevStatus > newExecutionStatus) { + executionStatus.compare_exchange_weak(prevStatus, newExecutionStatus); + } + if (OCLRT::DebugManager.flags.EventsTrackerEnable.get()) { + EventsTracker::getEventsTracker().notifyTransitionedExecutionStatus(); + } +} + void Event::submitCommand(bool abortTasks) { std::unique_ptr cmdToProcess(cmdToSubmit.exchange(nullptr)); if (cmdToProcess.get() != nullptr) { diff --git a/runtime/event/event.h b/runtime/event/event.h index 10ae28bc87..81c3683f01 100644 --- a/runtime/event/event.h +++ b/runtime/event/event.h @@ -339,14 +339,7 @@ class Event : public BaseObject<_cl_event>, public IDNode { // transitions event to new execution state // guarantees that newStatus <= oldStatus - void transitionExecutionStatus(int32_t newExecutionStatus) const { - int32_t prevStatus = executionStatus; - DBG_LOG(EventsDebugEnable, "transitionExecutionStatus event", this, " new status", newExecutionStatus, "previousStatus", prevStatus); - - while (prevStatus > newExecutionStatus) { - executionStatus.compare_exchange_weak(prevStatus, newExecutionStatus); - } - } + void transitionExecutionStatus(int32_t newExecutionStatus) const; //vector storing events that needs to be notified when this event is ready to go IFRefList childEventsToNotify; diff --git a/runtime/event/event_tracker.cpp b/runtime/event/event_tracker.cpp new file mode 100644 index 0000000000..aa5efbd7c2 --- /dev/null +++ b/runtime/event/event_tracker.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2018, Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "runtime/command_queue/command_queue.h" +#include "runtime/event/event.h" +#include "runtime/event/event_tracker.h" +#include "runtime/utilities/iflist.h" +#include "runtime/helpers/cl_helper.h" + +namespace OCLRT { + +std::unique_ptr EventsTracker::globalEvTracker = nullptr; + +EventsTracker &EventsTracker::getEventsTracker() { + static std::mutex initMutex; + std::lock_guard autolock(initMutex); + + if (!EventsTracker::globalEvTracker) + EventsTracker::globalEvTracker = std::unique_ptr{new EventsTracker()}; + return *EventsTracker::globalEvTracker; +} + +void EventsTracker::shutdownGlobalEvTracker() { + EventsTracker::globalEvTracker.reset(); +} + +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(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 == Event::eventNotReady) { + out << "NOT_READY"; + } else { + out << taskCount; + } + + out << ", level="; + if (taskLevel == Event::eventNotReady) { + 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(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 = OCLRT::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 == Event::eventNotReady) { + out << "NOT_READY"; + } else { + out << taskCount; + } + + out << ", level="; + if (taskLevel == Event::eventNotReady) { + 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 &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; + } +} + +IFList *EventsTracker::getList() { + return &trackedEvents; +} + +TrackedEvent *EventsTracker::getNodes() { + return trackedEvents.detachNodes(); +} + +void EventsTracker::dump() { + static std::mutex mutex; + std::lock_guard lock(mutex); + auto time = std::chrono::system_clock::now(); + std::string dumpFileName = "eg_" + "reg" + + std::to_string(reinterpret_cast(this)) + "_" + std::to_string(time.time_since_epoch().count()) + ".gv"; + std::shared_ptr 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 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::shared_ptr EventsTracker::createDumpStream(const std::string &filename) { + std::shared_ptr out{new std::fstream(filename, std::ios::binary | std::ios::out)}; + return out; +} + +} // namespace OCLRT diff --git a/runtime/event/event_tracker.h b/runtime/event/event_tracker.h new file mode 100644 index 0000000000..2e251b6724 --- /dev/null +++ b/runtime/event/event_tracker.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#include +#include + +namespace OCLRT { + +struct TrackedEvent : IFNode { + TrackedEvent(Event *ev, int64_t eventId) + : ev(ev), eventId(eventId) { + } + Event *ev = nullptr; + int64_t eventId = 1; +}; + +class EventsTracker { + + using EventIdMap = std::unordered_map; + using CmdqSet = std::set; + + protected: + std::atomic eventId{0}; + static std::unique_ptr globalEvTracker; + IFList trackedEvents; + EventsTracker() = default; + + public: + MOCKABLE_VIRTUAL ~EventsTracker() = default; + IFList *getList(); + MOCKABLE_VIRTUAL TrackedEvent *getNodes(); + void dump(); + void notifyCreation(Event *eventToTrack); + void notifyDestruction(Event *eventToDestroy); + void notifyTransitionedExecutionStatus(); + MOCKABLE_VIRTUAL std::shared_ptr createDumpStream(const std::string &filename); + static EventsTracker &getEventsTracker(); + static void shutdownGlobalEvTracker(); + static std::string label(Event *node, const EventIdMap &eventsIdMapping); + static std::string label(CommandQueue *cmdQ); + static void dumpQueue(CommandQueue *cmdQ, std::ostream &out, CmdqSet &dumpedCmdQs); + static void dumpEdge(Event *leftNode, Event *rightNode, std::ostream &out, const EventIdMap &eventsIdMapping); + static void dumpNode(Event *node, std::ostream &out, const EventIdMap &eventsIdMapping); + static void dumpGraph(Event *node, std::ostream &out, CmdqSet &dumpedCmdQs, std::set &dumpedEvents, + const EventIdMap &eventsIdMapping); +}; + +} // namespace OCLRT diff --git a/runtime/helpers/CMakeLists.txt b/runtime/helpers/CMakeLists.txt index 31f20a7f11..95d9c9028d 100644 --- a/runtime/helpers/CMakeLists.txt +++ b/runtime/helpers/CMakeLists.txt @@ -31,6 +31,7 @@ set(RUNTIME_SRCS_HELPERS_BASE ${CMAKE_CURRENT_SOURCE_DIR}/built_ins_helper.h ${CMAKE_CURRENT_SOURCE_DIR}/cache_policy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/cache_policy.h + ${CMAKE_CURRENT_SOURCE_DIR}/cl_helper.h ${CMAKE_CURRENT_SOURCE_DIR}/completion_stamp.h ${CMAKE_CURRENT_SOURCE_DIR}/convert_color.h ${CMAKE_CURRENT_SOURCE_DIR}/debug_helpers.h diff --git a/runtime/helpers/cl_helper.h b/runtime/helpers/cl_helper.h new file mode 100644 index 0000000000..0418f0dde8 --- /dev/null +++ b/runtime/helpers/cl_helper.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018, Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "CL/cl.h" +#include "CL/cl_gl_ext.h" + +namespace OCLRT { +inline const std::string cmdTypetoString(cl_command_type cmd) { + switch (cmd) { + case CL_COMMAND_NDRANGE_KERNEL: + return "CL_COMMAND_NDRANGE_KERNEL"; + case CL_COMMAND_TASK: + return "CL_COMMAND_TASK"; + case CL_COMMAND_NATIVE_KERNEL: + return "CL_COMMAND_NATIVE_KERNEL"; + case CL_COMMAND_READ_BUFFER: + return "CL_COMMAND_READ_BUFFER"; + case CL_COMMAND_WRITE_BUFFER: + return "CL_COMMAND_WRITE_BUFFER"; + case CL_COMMAND_COPY_BUFFER: + return "CL_COMMAND_COPY_BUFFER"; + case CL_COMMAND_READ_IMAGE: + return "CL_COMMAND_READ_IMAGE"; + case CL_COMMAND_WRITE_IMAGE: + return "CL_COMMAND_WRITE_IMAGE"; + case CL_COMMAND_COPY_IMAGE: + return "CL_COMMAND_COPY_IMAGE"; + case CL_COMMAND_COPY_IMAGE_TO_BUFFER: + return "CL_COMMAND_COPY_IMAGE_TO_BUFFER"; + case CL_COMMAND_COPY_BUFFER_TO_IMAGE: + return "CL_COMMAND_COPY_BUFFER_TO_IMAGE"; + case CL_COMMAND_MAP_BUFFER: + return "CL_COMMAND_MAP_BUFFER"; + case CL_COMMAND_MAP_IMAGE: + return "CL_COMMAND_MAP_IMAGE"; + case CL_COMMAND_UNMAP_MEM_OBJECT: + return "CL_COMMAND_UNMAP_MEM_OBJECT"; + case CL_COMMAND_MARKER: + return "CL_COMMAND_MARKER"; + case CL_COMMAND_ACQUIRE_GL_OBJECTS: + return "CL_COMMAND_ACQUIRE_GL_OBJECTS"; + case CL_COMMAND_RELEASE_GL_OBJECTS: + return "CL_COMMAND_RELEASE_GL_OBJECTS"; + case CL_COMMAND_READ_BUFFER_RECT: + return "CL_COMMAND_READ_BUFFER_RECT"; + case CL_COMMAND_WRITE_BUFFER_RECT: + return "CL_COMMAND_WRITE_BUFFER_RECT"; + case CL_COMMAND_COPY_BUFFER_RECT: + return "CL_COMMAND_COPY_BUFFER_RECT"; + case CL_COMMAND_USER: + return "CL_COMMAND_USER"; + case CL_COMMAND_BARRIER: + return "CL_COMMAND_BARRIER"; + case CL_COMMAND_MIGRATE_MEM_OBJECTS: + return "CL_COMMAND_MIGRATE_MEM_OBJECTS"; + case CL_COMMAND_FILL_BUFFER: + return "CL_COMMAND_FILL_BUFFER"; + case CL_COMMAND_FILL_IMAGE: + return "CL_COMMAND_FILL_IMAGE"; + case CL_COMMAND_SVM_FREE: + return "CL_COMMAND_SVM_FREE"; + case CL_COMMAND_SVM_MEMCPY: + return "CL_COMMAND_SVM_MEMCPY"; + case CL_COMMAND_SVM_MEMFILL: + return "CL_COMMAND_SVM_MEMFILL"; + case CL_COMMAND_SVM_MAP: + return "CL_COMMAND_SVM_MAP"; + case CL_COMMAND_SVM_UNMAP: + return "CL_COMMAND_SVM_UNMAP"; + case CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR: + return "CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR"; + default: { + std::string returnString("CMD_UNKNOWN:" + std::to_string((cl_command_type)cmd)); + return returnString; + } + } +} +} // namespace OCLRT \ No newline at end of file diff --git a/runtime/os_interface/DebugVariables.inl b/runtime/os_interface/DebugVariables.inl index e6cc56bcac..3d784cf3a5 100644 --- a/runtime/os_interface/DebugVariables.inl +++ b/runtime/os_interface/DebugVariables.inl @@ -57,6 +57,7 @@ DECLARE_DEBUG_VARIABLE(bool, LogAlignedAllocations, false, "Logs alignedMalloc a DECLARE_DEBUG_VARIABLE(bool, LogMemoryObject, false, "Logs memory object ptrs, sizes and operations") DECLARE_DEBUG_VARIABLE(bool, ResidencyDebugEnable, 0, "enables debug messages and checks for Residency Model") DECLARE_DEBUG_VARIABLE(bool, EventsDebugEnable, 0, "enables debug messages for events, virtual events, blocked enqueues, events trees etc.") +DECLARE_DEBUG_VARIABLE(bool, EventsTrackerEnable, false, "enables event graphs dumping") DECLARE_DEBUG_VARIABLE(bool, PrintEMDebugInformation, false, "prints execution model related debug information") DECLARE_DEBUG_VARIABLE(bool, PrintLWSSizes, false, "prints driver choosen local workgroup sizes") DECLARE_DEBUG_VARIABLE(bool, PrintDispatchParameters, false, "prints dispatch paramters of kernels passed to clEnqueueNDRangeKernel") diff --git a/unit_tests/event/CMakeLists.txt b/unit_tests/event/CMakeLists.txt index 0c82627eb2..ce0b09fb5e 100644 --- a/unit_tests/event/CMakeLists.txt +++ b/unit_tests/event/CMakeLists.txt @@ -26,6 +26,7 @@ set(IGDRCL_SRCS_tests_event ${CMAKE_CURRENT_SOURCE_DIR}/event_fixture.h ${CMAKE_CURRENT_SOURCE_DIR}/event_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/event_tests_mt.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/event_tracker_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/user_events_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/user_events_tests_mt.cpp ) diff --git a/unit_tests/event/event_tracker_tests.cpp b/unit_tests/event/event_tracker_tests.cpp new file mode 100644 index 0000000000..f8960852aa --- /dev/null +++ b/unit_tests/event/event_tracker_tests.cpp @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2018, Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "event_fixture.h" +#include "runtime/event/event_tracker.h" +#include "runtime/event/event.h" +#include "runtime/helpers/file_io.h" +#include "unit_tests/helpers/debug_manager_state_restore.h" + +#include +#include + +TEST(EventsTracker, whenCallingGetEventsTrackerThenGetGlobalEventsTrackerInstance) { + auto &evTracker1 = EventsTracker::getEventsTracker(); + auto &evTracker2 = EventsTracker::getEventsTracker(); + + EXPECT_EQ(&evTracker1, &evTracker2); + + EventsTracker::shutdownGlobalEvTracker(); +} + +TEST(EventsTracker, whenCallLabelFunctionThenGetStringWithProperEventId) { + UserEvent uEvent; + + std::unordered_map map; + map[&uEvent] = 0; + + EXPECT_STREQ("e0", EventsTracker::label(&uEvent, map).c_str()); +} + +TEST(EventsTracker, whenCallLabelFunctionWhenEventIsNotInMapThenGetStringWithoutId) { + UserEvent uEvent; + + std::unordered_map map; + + EXPECT_STREQ("e", EventsTracker::label(&uEvent, map).c_str()); +} + +TEST(EventsTracker, whenCallLabelFunctionThenGetStringWithProperCmdqId) { + CommandQueue cmdq; + + std::string expect = "cq" + std::to_string(reinterpret_cast(&cmdq)); + + EXPECT_STREQ(expect.c_str(), EventsTracker::label(&cmdq).c_str()); +} + +TEST(EventsTracker, givenNullptrCmdqThenNotDumping) { + CommandQueue *cmdq_ptr = nullptr; + + std::stringstream stream; + std::set dumped; + + EventsTracker::dumpQueue(cmdq_ptr, stream, dumped); + + EXPECT_STREQ("", stream.str().c_str()); +} + +TEST(EventsTracker, givenAlreadyDumpedCmdqThenNotDumping) { + CommandQueue cmdq; + + std::stringstream stream; + std::set dumped; + dumped.insert(&cmdq); + + EventsTracker::dumpQueue(&cmdq, stream, dumped); + + EXPECT_STREQ("", stream.str().c_str()); +} + +TEST(EventsTracker, givenCmqdWithTaskCountAndLevelNotReadyThenDumpingCmdqWithNotReadyLabels) { + CommandQueue cmdq; + cmdq.taskCount = Event::eventNotReady; + cmdq.taskLevel = Event::eventNotReady; + + std::stringstream stream; + std::set dumped; + + EventsTracker::dumpQueue(&cmdq, stream, dumped); + + std::stringstream expected; + expected << EventsTracker::label(&cmdq) << "[label=\"{------CmdQueue, ptr=" << &cmdq << "------|task count=NOT_READY, level=NOT_READY}\",color=blue];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, whenCallDumpQueueThenDumpingCmdqWithProperCountTaskAndLevelValues) { + CommandQueue cmdq; + cmdq.taskCount = 3; + cmdq.taskLevel = 1; + + std::stringstream stream; + std::set dumped; + + EventsTracker::dumpQueue(&cmdq, stream, dumped); + + std::stringstream expected; + expected << EventsTracker::label(&cmdq) << "[label=\"{------CmdQueue, ptr=" << &cmdq << "------|task count=3, level=1}\",color=blue];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, whenCallDumpEdgeThenGetStringWithProperLabelOfDumpedEdge) { + UserEvent uEvent1; + UserEvent uEvent2; + + std::stringstream stream; + std::unordered_map map; + map[&uEvent1] = 0; + map[&uEvent2] = 1; + + EventsTracker::dumpEdge(&uEvent1, &uEvent2, stream, map); + + EXPECT_STREQ("e0->e1;\n", stream.str().c_str()); +} + +TEST(EventsTracker, givenEventWithTaskLevelAndCountNotReadyThenDumpingNodeWithNotReadyLabels) { + UserEvent uEvent; + uEvent.taskLevel = Event::eventNotReady; + uEvent.updateTaskCount(Event::eventNotReady); + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + + EventsTracker::dumpNode(&uEvent, stream, map); + + std::stringstream expected; + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, whenCallDumpNodeFunctionThenDumpingNodeWithProperTaskLevelAndCountValues) { + UserEvent uEvent; + uEvent.taskLevel = 1; + uEvent.updateTaskCount(1); + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + + EventsTracker::dumpNode(&uEvent, stream, map); + + std::stringstream expected; + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent << "------||CL_QUEUED|task count=1, level=1|CALLBACKS=FALSE}\",color=red];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenNullptrEventThenNotDumpingNode) { + UserEvent *uEvent = nullptr; + + std::stringstream stream; + std::unordered_map map; + map[uEvent] = 0; + + EventsTracker::dumpNode(uEvent, stream, map); + + EXPECT_STREQ("eNULL[label=\"{ptr=nullptr}\",color=red];\n", stream.str().c_str()); +} + +TEST(EventsTracker, givenEventAndUserEventThenDumpingNodeWithProperLabels) { + UserEvent uEvent; + Event event(nullptr, CL_COMMAND_NDRANGE_KERNEL, Event::eventNotReady, Event::eventNotReady); + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + map[&event] = 1; + + EventsTracker::dumpNode(&uEvent, stream, map); + + std::stringstream expecteduEvent; + expecteduEvent << "e0[label=\"{------USER_EVENT ptr=" << &uEvent << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n"; + + EXPECT_STREQ(expecteduEvent.str().c_str(), stream.str().c_str()); + + stream.str(std::string()); + EventsTracker::dumpNode(&event, stream, map); + + std::stringstream expectedEvent; + expectedEvent << "e1[label=\"{-----------EVENT ptr=" << &event + << "------|CL_COMMAND_NDRANGE_KERNEL|CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n"; + + EXPECT_STREQ(expectedEvent.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenCmdqAndItsVirtualEventThenDumpingWithProperLabels) { + MockContext ctx; + CommandQueue cmdq; + VirtualEvent vEvent(&cmdq, &ctx); + vEvent.setCurrentCmdQVirtualEvent(true); + vEvent.updateTaskCount(1); + + std::stringstream stream; + std::unordered_map map; + map[&vEvent] = 0; + + EventsTracker::dumpNode(&vEvent, stream, map); + + std::stringstream expected; + expected << "e0[label=\"{---------V_EVENT ptr=" << &vEvent << "------|CMD_UNKNOWN:" << (cl_command_type)-1 + << "|CL_QUEUED|task count=1, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n" + << EventsTracker::label(&cmdq) << "->e0[label=\"VIRTUAL_EVENT\"];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenEventWithCallbackThenDumpingWithProperLabel) { + Event::Callback::ClbFuncT func = [](cl_event ev, cl_int i, void *data) {}; + UserEvent uEvent; + uEvent.addCallback(func, 0, nullptr); + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + + EventsTracker::dumpNode(&uEvent, stream, map); + + std::stringstream expected; + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=TRUE}\",color=red];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenSubmittedEventThenDumpingWithProperLabel) { + Event event(nullptr, CL_COMMAND_NDRANGE_KERNEL, Event::eventNotReady, Event::eventNotReady); + + std::stringstream stream; + std::unordered_map map; + map[&event] = 0; + std::stringstream expected; + + event.setStatus(CL_SUBMITTED); + EventsTracker::dumpNode(&event, stream, map); + + expected << "e0[label=\"{-----------EVENT ptr=" << &event + << "------|CL_COMMAND_NDRANGE_KERNEL|CL_SUBMITTED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=yellow];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenSubmittedUserEventThenDumpingWithProperLabel) { + UserEvent uEvent; + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + std::stringstream expected; + + uEvent.setStatus(CL_SUBMITTED); + EventsTracker::dumpNode(&uEvent, stream, map); + + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent + << "------||CL_SUBMITTED|task count=NOT_READY, level=0|CALLBACKS=FALSE}\",color=red];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenUserEventWithUnproperStatusThenDumpingWithProperLabel) { + UserEvent uEvent; + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + std::stringstream expected; + + uEvent.setStatus(-1); + EventsTracker::dumpNode(&uEvent, stream, map); + + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent + << "------||ABORTED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=green];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenRunningEventThenDumpingWithProperLabel) { + UserEvent uEvent; + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + std::stringstream expected; + + uEvent.setStatus(CL_RUNNING); + EventsTracker::dumpNode(&uEvent, stream, map); + + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent << "------||CL_RUNNING|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} +TEST(EventsTracker, givenQueuedEventThenDumpingWithProperLabel) { + UserEvent uEvent; + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + std::stringstream expected; + + uEvent.setStatus(CL_QUEUED); + EventsTracker::dumpNode(&uEvent, stream, map); + + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} +TEST(EventsTracker, givenCompleteEventThenDumpingWithProperLabel) { + UserEvent uEvent; + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + std::stringstream expected; + + uEvent.setStatus(CL_COMPLETE); + EventsTracker::dumpNode(&uEvent, stream, map); + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent << "------||CL_COMPLETE|task count=NOT_READY, level=0|CALLBACKS=FALSE}\",color=green];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenNullptrEventThenNotDumpingGraph) { + Event *ev = nullptr; + + std::stringstream stream; + std::unordered_map map; + map[ev] = 0; + std::set dumpedCmdQs; + std::set dumpedEvents; + + EventsTracker::dumpGraph(ev, stream, dumpedCmdQs, dumpedEvents, map); + + EXPECT_STREQ("", stream.str().c_str()); +} + +TEST(EventsTracker, givenAlreadyDumpedEventThenNotDumpingGraph) { + UserEvent uEvent; + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + std::set dumpedCmdQs; + std::set dumpedEvents; + + dumpedEvents.insert(&uEvent); + EventsTracker::dumpGraph(&uEvent, stream, dumpedCmdQs, dumpedEvents, map); + + EXPECT_STREQ("", stream.str().c_str()); +} + +TEST(EventsTracker, givenCmdqAndItsVirtualEventThenDumpingProperGraph) { + MockContext ctx; + CommandQueue cmdq; + VirtualEvent vEvent(&cmdq, &ctx); + vEvent.setCurrentCmdQVirtualEvent(true); + vEvent.updateTaskCount(1); + + std::stringstream stream; + std::unordered_map map; + map[&vEvent] = 0; + std::set dumpedCmdQs; + std::set dumpedEvents; + + EventsTracker::dumpGraph(&vEvent, stream, dumpedCmdQs, dumpedEvents, map); + std::stringstream expected; + expected << EventsTracker::label(&cmdq) << "[label=\"{------CmdQueue, ptr=" << &cmdq << "------|task count=0, level=0}\",color=blue];\ne0[label=\"{---------V_EVENT ptr=" << &vEvent + << "------|CMD_UNKNOWN:4294967295|CL_QUEUED|task count=1, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n" + << EventsTracker::label(&cmdq) << "->e0[label=\"VIRTUAL_EVENT\"];\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); +} + +TEST(EventsTracker, givenTwoEventsWithCommonParentEventThenDumpingProperGraph) { + UserEvent uEvent, uEventChild1, uEventChild2; + uEvent.addChild(uEventChild1); + uEvent.addChild(uEventChild2); + + std::stringstream stream; + std::unordered_map map; + map[&uEvent] = 0; + map[&uEventChild1] = 1; + map[&uEventChild2] = 2; + std::set dumpedCmdQs; + std::set dumpedEvents; + + EventsTracker::dumpGraph(&uEvent, stream, dumpedCmdQs, dumpedEvents, map); + std::stringstream expected; + expected << "e0[label=\"{------USER_EVENT ptr=" << &uEvent + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne2[label=\"{------USER_EVENT ptr=" << &uEventChild2 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0->e2;\ne1[label=\"{------USER_EVENT ptr=" << &uEventChild1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0->e1;\n"; + + EXPECT_STREQ(expected.str().c_str(), stream.str().c_str()); + + uEvent.setStatus(0); +} + +TEST(EventsTracker, whenCalingCreateDumpStreamThenGettingValidFstreamInstance) { + std::string testFileName("test_files\\EventsTracker_testfile.gv"); + std::shared_ptr stream = EventsTracker::getEventsTracker().createDumpStream(testFileName); + + EXPECT_TRUE(stream->good()); + + static_cast(stream.get())->close(); + remove(testFileName.c_str()); + EventsTracker::shutdownGlobalEvTracker(); +} + +class EventsTrackerMock : public EventsTracker { + public: + std::shared_ptr createDumpStream(const std::string &filename) override { + streamMock.reset(); + std::shared_ptr out{new std::stringstream()}; + streamMock = out; + return out; + } + void overrideGlobal() { + originGlobal.swap(EventsTracker::globalEvTracker); + EventsTracker::globalEvTracker = std::unique_ptr{new EventsTrackerMock()}; + } + void restoreGlobal() { + EventsTracker::shutdownGlobalEvTracker(); + EventsTracker::globalEvTracker.swap(originGlobal); + } + std::shared_ptr streamMock; + std::unique_ptr originGlobal; +}; + +TEST(EventsTracker, whenDeletingEventTwoTimesThenDeletingIsProper) { + UserEvent uEvent1; + EventsTrackerMock evTrackerMock; + + std::stringstream expected; + + evTrackerMock.getList()->pushFrontOne(*new TrackedEvent{&uEvent1, 1}); + evTrackerMock.getList()->pushFrontOne(*new TrackedEvent{&uEvent1, -2}); + evTrackerMock.getList()->pushFrontOne(*new TrackedEvent{&uEvent1, -3}); + evTrackerMock.dump(); + + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); +} + +TEST(EventsTracker, givenTwoEventsWithSamePtrWhenFirstOneIsDeletedThenDumpingFirstProperly) { + UserEvent uEvent; + EventsTrackerMock evTrackerMock; + + std::stringstream expected; + + evTrackerMock.getList()->pushFrontOne(*new TrackedEvent{&uEvent, 2}); + evTrackerMock.getList()->pushFrontOne(*new TrackedEvent{&uEvent, -1}); + evTrackerMock.dump(); + + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne2[label=\"{------USER_EVENT ptr=" + << &uEvent << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); +} + +TEST(EventsTracker, whenNotifyCreationOfEventThenEventIsDumped) { + Event event(nullptr, CL_COMMAND_USER, Event::eventNotReady, Event::eventNotReady); + EventsTrackerMock evTrackerMock; + + std::stringstream expected; + + evTrackerMock.notifyCreation(&event); + + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); +} + +TEST(EventsTracker, whenNotifyTransitionedExecutionStatusOfEventThenEventIsDumpedWithProperDescription) { + UserEvent uEvent; + EventsTrackerMock evTrackerMock; + + evTrackerMock.notifyCreation(&uEvent); + evTrackerMock.notifyTransitionedExecutionStatus(); + + std::stringstream expected; + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne0[label=\"{------USER_EVENT ptr=" << &uEvent + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); +} + +TEST(EventsTracker, whenNotifyDestructionOfEventThenEventIsDumped) { + UserEvent *uEvent = new UserEvent(); + EventsTrackerMock evTrackerMock; + + evTrackerMock.notifyCreation(uEvent); + evTrackerMock.notifyDestruction(uEvent); + delete uEvent; + + std::stringstream stream; + stream << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\n\n}\n"; + + EXPECT_STREQ(stream.str().c_str(), evTrackerMock.streamMock->str().c_str()); +} + +TEST(EventsTracker, givenSeveralEventsWhenOneIsCompleteThenDumpingWithProperLabels) { + UserEvent *uEvent1 = new UserEvent(); + UserEvent *uEvent2 = new UserEvent(); + UserEvent *uEvent3 = new UserEvent(); + EventsTrackerMock evTrackerMock; + + evTrackerMock.notifyCreation(uEvent1); + evTrackerMock.notifyCreation(uEvent2); + evTrackerMock.notifyCreation(uEvent3); + uEvent2->setStatus(CL_COMPLETE); + evTrackerMock.notifyTransitionedExecutionStatus(); + evTrackerMock.notifyDestruction(uEvent2); + delete uEvent2; + + std::stringstream stream; + stream << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne2[label=\"{------USER_EVENT ptr=" << uEvent3 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0[label=\"{------USER_EVENT ptr=" << uEvent1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n\n}\n"; + + EXPECT_STREQ(stream.str().c_str(), evTrackerMock.streamMock->str().c_str()); + delete uEvent1; + delete uEvent3; +} + +TEST(EventsTracker, givenEventsWithDependenciesBetweenThemThenDumpingProperGraph) { + EventsTrackerMock evTrackerMock; + + UserEvent uEvent1; + evTrackerMock.notifyCreation(&uEvent1); + evTrackerMock.dump(); + + std::stringstream expected; + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne0[label=\"{------USER_EVENT ptr=" << &uEvent1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); + + UserEvent uEvent2; + evTrackerMock.notifyCreation(&uEvent2); + evTrackerMock.dump(); + + expected.str(std::string()); + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne1[label=\"{------USER_EVENT ptr=" << &uEvent2 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0[label=\"{------USER_EVENT ptr=" << &uEvent1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); + + UserEvent uEventChild1; + evTrackerMock.notifyCreation(&uEventChild1); + uEvent1.addChild(uEventChild1); + evTrackerMock.dump(); + + expected.str(std::string()); + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne1[label=\"{------USER_EVENT ptr=" << &uEvent2 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0[label=\"{------USER_EVENT ptr=" << &uEvent1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne2[label=\"{------USER_EVENT ptr=" << &uEventChild1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0->e2;\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); + + UserEvent uEventChild2; + evTrackerMock.notifyCreation(&uEventChild2); + uEvent1.addChild(uEventChild2); + evTrackerMock.dump(); + + expected.str(std::string()); + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne1[label=\"{------USER_EVENT ptr=" << &uEvent2 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0[label=\"{------USER_EVENT ptr=" << &uEvent1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne3[label=\"{------USER_EVENT ptr=" << &uEventChild2 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0->e3;\ne2[label=\"{------USER_EVENT ptr=" << &uEventChild1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0->e2;\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); + + uEvent2.addChild(uEvent1); + evTrackerMock.dump(); + + expected.str(std::string()); + expected << "digraph events_registry_" << &evTrackerMock << " {\nnode [shape=record]\n//pragma: somePragmaData\ne1[label=\"{------USER_EVENT ptr=" << &uEvent2 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0[label=\"{------USER_EVENT ptr=" << &uEvent1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne3[label=\"{------USER_EVENT ptr=" << &uEventChild2 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0->e3;\ne2[label=\"{------USER_EVENT ptr=" << &uEventChild1 + << "------||CL_QUEUED|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\ne0->e2;\ne1->e0;\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMock.streamMock->str().c_str()); + + uEvent2.setStatus(0); + uEvent1.setStatus(0); +} + +TEST(EventsTracker, whenEventsDebugEnableFlagIsTrueAndCreateOrChangeStatusOrDestroyEventThenDumpingGraph) { + DebugManagerStateRestore dbRestore; + DebugManager.flags.EventsTrackerEnable.set(true); + + EventsTrackerMock evTrackerMock; + evTrackerMock.overrideGlobal(); + + Event *ev = new Event(nullptr, CL_COMMAND_NDRANGE_KERNEL, Event::eventNotReady, Event::eventNotReady); + + std::stringstream expected; + expected << "digraph events_registry_" << &EventsTracker::getEventsTracker() << " {\nnode [shape=record]\n//pragma: somePragmaData\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), static_cast(&EventsTracker::getEventsTracker())->streamMock->str().c_str()); + + ev->setStatus(1); + + expected.str(std::string()); + expected << "digraph events_registry_" << &EventsTracker::getEventsTracker() << " {\nnode [shape=record]\n//pragma: somePragmaData\ne0[label=\"{-----------EVENT ptr=" << ev + << "------|CL_COMMAND_NDRANGE_KERNEL|CL_RUNNING|task count=NOT_READY, level=NOT_READY|CALLBACKS=FALSE}\",color=red];\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), static_cast(&EventsTracker::getEventsTracker())->streamMock->str().c_str()); + + delete ev; + + expected.str(std::string()); + expected << "digraph events_registry_" << &EventsTracker::getEventsTracker() << " {\nnode [shape=record]\n//pragma: somePragmaData\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), static_cast(&EventsTracker::getEventsTracker())->streamMock->str().c_str()); + + evTrackerMock.restoreGlobal(); +} + +class EventsTrackerMockMT : public EventsTrackerMock { + public: + TrackedEvent *getNodes() override { + auto TrackedEventsMock = std::shared_ptr>{new IFList}; + return TrackedEventsMock->detachNodes(); + } + std::shared_ptr> *TrackedEventsMock; +}; + +TEST(EventsTracker, givenEventsFromDifferentThreadsThenDumpingProperly) { + auto evTrackerMockMT = std::shared_ptr{new EventsTrackerMockMT()}; + UserEvent uEvent1; + UserEvent uEvent2; + + evTrackerMockMT->getList()->pushFrontOne(*new TrackedEvent{&uEvent1, 2}); + evTrackerMockMT->getList()->pushFrontOne(*new TrackedEvent{&uEvent2, 3}); + evTrackerMockMT->dump(); + + std::stringstream expected; + expected << "digraph events_registry_" << evTrackerMockMT + << " {\nnode [shape=record]\n//pragma: somePragmaData\n\n}\n"; + + EXPECT_STREQ(expected.str().c_str(), evTrackerMockMT->streamMock->str().c_str()); +} diff --git a/unit_tests/helpers/CMakeLists.txt b/unit_tests/helpers/CMakeLists.txt index 66774d01c5..057b0d3a3c 100644 --- a/unit_tests/helpers/CMakeLists.txt +++ b/unit_tests/helpers/CMakeLists.txt @@ -25,6 +25,7 @@ set(IGDRCL_SRCS_tests_helpers ${CMAKE_CURRENT_SOURCE_DIR}/base_object_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base_object_tests_mt.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basic_math_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/cl_helper_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/debug_helpers_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/debug_manager_state_restore.h ${CMAKE_CURRENT_SOURCE_DIR}/dirty_state_helpers_tests.cpp diff --git a/unit_tests/helpers/cl_helper_tests.cpp b/unit_tests/helpers/cl_helper_tests.cpp new file mode 100644 index 0000000000..23682297c4 --- /dev/null +++ b/unit_tests/helpers/cl_helper_tests.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018, Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gtest/gtest.h" +#include "runtime/helpers/cl_helper.h" + +#include + +TEST(ClHelper, whenCallGetStringWithCmdTypeFunctionThenGetProperCmdTypeAsString) { + std::array expected = { + "CL_COMMAND_NDRANGE_KERNEL", + "CL_COMMAND_TASK", + "CL_COMMAND_NATIVE_KERNEL", + "CL_COMMAND_READ_BUFFER", + "CL_COMMAND_WRITE_BUFFER", + "CL_COMMAND_COPY_BUFFER", + "CL_COMMAND_READ_IMAGE", + "CL_COMMAND_WRITE_IMAGE", + "CL_COMMAND_COPY_IMAGE", + "CL_COMMAND_COPY_IMAGE_TO_BUFFER", + "CL_COMMAND_COPY_BUFFER_TO_IMAGE", + "CL_COMMAND_MAP_BUFFER", + "CL_COMMAND_MAP_IMAGE", + "CL_COMMAND_UNMAP_MEM_OBJECT", + "CL_COMMAND_MARKER", + "CL_COMMAND_ACQUIRE_GL_OBJECTS", + "CL_COMMAND_RELEASE_GL_OBJECTS", + "CL_COMMAND_READ_BUFFER_RECT", + "CL_COMMAND_WRITE_BUFFER_RECT", + "CL_COMMAND_COPY_BUFFER_RECT", + "CL_COMMAND_USER", + "CL_COMMAND_BARRIER", + "CL_COMMAND_MIGRATE_MEM_OBJECTS", + "CL_COMMAND_FILL_BUFFER", + "CL_COMMAND_FILL_IMAGE", + "CL_COMMAND_SVM_FREE", + "CL_COMMAND_SVM_MEMCPY", + "CL_COMMAND_SVM_MEMFILL", + "CL_COMMAND_SVM_MAP", + "CL_COMMAND_SVM_UNMAP"}; + + for (int i = CL_COMMAND_NDRANGE_KERNEL; i <= CL_COMMAND_SVM_UNMAP; i++) { + EXPECT_STREQ(expected[i - CL_COMMAND_NDRANGE_KERNEL].c_str(), OCLRT::cmdTypetoString(i).c_str()); + } + + std::stringstream stream; + stream << "CMD_UNKNOWN:" << (cl_command_type)-1; + + EXPECT_STREQ(stream.str().c_str(), OCLRT::cmdTypetoString(-1).c_str()); + + EXPECT_STREQ("CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR", OCLRT::cmdTypetoString(CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR).c_str()); +} \ No newline at end of file diff --git a/unit_tests/test_files/igdrcl.config b/unit_tests/test_files/igdrcl.config index 5b82418cc9..e52eda22eb 100644 --- a/unit_tests/test_files/igdrcl.config +++ b/unit_tests/test_files/igdrcl.config @@ -52,6 +52,7 @@ ProductFamilyOverride = unk EnableDebugBreak = true EnableComputeWorkSizeND = true EventsDebugEnable = false +EventsTrackerEnable = false UseMaxSimdSizeToDeduceMaxWorkgroupSize = false EnableComputeWorkSizeSquared = false TrackParentEvents = false