diff --git a/level_zero/api/driver_experimental/public/zex_graph.cpp b/level_zero/api/driver_experimental/public/zex_graph.cpp index 0246fb85e2..b1bb0e534e 100644 --- a/level_zero/api/driver_experimental/public/zex_graph.cpp +++ b/level_zero/api/driver_experimental/public/zex_graph.cpp @@ -196,21 +196,7 @@ ze_result_t ZE_APICALL zeGraphIsEmptyExp(ze_graph_handle_t hGraph) { } ze_result_t ZE_APICALL zeGraphDumpContentsExp(ze_graph_handle_t hGraph, const char *filePath, void *pNext) { - if (nullptr != pNext) { - return ZE_RESULT_ERROR_INVALID_ARGUMENT; - } - - auto graph = L0::Graph::fromHandle(hGraph); - if (nullptr == graph) { - return ZE_RESULT_ERROR_INVALID_ARGUMENT; - } - - if (nullptr == filePath) { - return ZE_RESULT_ERROR_INVALID_ARGUMENT; - } - - L0::GraphDotExporter exporter{}; - return exporter.exportToFile(*graph, filePath); + return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; } } // namespace L0 diff --git a/level_zero/core/test/black_box_tests/zello_graph.cpp b/level_zero/core/test/black_box_tests/zello_graph.cpp index 06b43b7e7e..8a187c24b2 100644 --- a/level_zero/core/test/black_box_tests/zello_graph.cpp +++ b/level_zero/core/test/black_box_tests/zello_graph.cpp @@ -11,9 +11,9 @@ #include "zello_compile.h" #include +#include #include - -#define ENABLE_GRAPH_DUMP true +#include using zeGraphCreateExpFP = ze_result_t(ZE_APICALL *)(ze_context_handle_t context, ze_graph_handle_t *phGraph, void *pNext); using zeCommandListBeginGraphCaptureExpFP = ze_result_t(ZE_APICALL *)(ze_command_list_handle_t hCommandList, void *pNext); @@ -48,21 +48,6 @@ struct GraphApi { } }; -void dumpGraphToDotIfEnabled(const GraphApi &graphApi, ze_graph_handle_t virtualGraph, const std::string &testName) { - if (!ENABLE_GRAPH_DUMP) { - return; - } - - std::string filename = testName + "_graph.gv"; - ze_result_t dumpResult = graphApi.graphDumpContents(virtualGraph, filename.c_str(), nullptr); - - if (dumpResult == ZE_RESULT_SUCCESS) { - std::cout << "Graph dumped to " << filename << std::endl; - } else { - std::cerr << "Failed to dump graph for test " << testName << " (result: " << std::hex << dumpResult << ")" << std::endl; - } -} - GraphApi loadGraphApi(ze_driver_handle_t driver) { GraphApi ret; zeDriverGetExtensionFunctionAddress(driver, "zeGraphCreateExp", reinterpret_cast(&ret.graphCreate)); @@ -145,8 +130,6 @@ void testAppendMemoryCopy(ze_driver_handle_t driver, ze_context_handle_t &contex std::cerr << "stackBuffer == " << static_cast(stackBuffer) << std::endl; } - dumpGraphToDotIfEnabled(graphApi, virtualGraph, __func__); - delete[] heapBuffer; SUCCESS_OR_TERMINATE(zeMemFree(context, zeBuffer)); @@ -242,8 +225,6 @@ void testMultiGraph(ze_driver_handle_t driver, ze_context_handle_t &context, ze_ std::cerr << "stackBuffer == " << static_cast(stackBuffer) << std::endl; } - dumpGraphToDotIfEnabled(graphApi, virtualGraph, __func__); - delete[] heapBuffer; SUCCESS_OR_TERMINATE(zeMemFree(context, zeBuffer)); @@ -407,8 +388,6 @@ void testAppendLaunchKernel(ze_driver_handle_t driver, ze_context_handle_t &cont std::cerr << "outputData == " << static_cast(outputData.get()) << std::endl; } - dumpGraphToDotIfEnabled(graphApi, virtualGraph, __func__); - // Cleanup SUCCESS_OR_TERMINATE(zeMemFree(context, dstBuffer)); SUCCESS_OR_TERMINATE(zeMemFree(context, interimBuffer)); diff --git a/level_zero/core/test/unit_tests/experimental/CMakeLists.txt b/level_zero/core/test/unit_tests/experimental/CMakeLists.txt index 089ffa12dd..7d8f5d67ed 100644 --- a/level_zero/core/test/unit_tests/experimental/CMakeLists.txt +++ b/level_zero/core/test/unit_tests/experimental/CMakeLists.txt @@ -7,7 +7,6 @@ target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/test_graph.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_graph_exporter.cpp ) target_include_directories(${TARGET_NAME} diff --git a/level_zero/core/test/unit_tests/experimental/test_graph.cpp b/level_zero/core/test/unit_tests/experimental/test_graph.cpp index b11c7f1321..be11d5b21a 100644 --- a/level_zero/core/test/unit_tests/experimental/test_graph.cpp +++ b/level_zero/core/test/unit_tests/experimental/test_graph.cpp @@ -328,6 +328,14 @@ TEST(GraphTestDebugApis, GivenNonEmptyGraphWhenGraphIsEmptyIsCalledThenErrorIsRe EXPECT_EQ(ZE_RESULT_QUERY_FALSE, ::zeGraphIsEmptyExp(&srcGraph)); } +TEST(GraphTestDebugApis, WhenGraphDumpContentsIsCalledThenReturnUnsupportedFeature) { + GraphsCleanupGuard graphCleanup; + Mock ctx; + L0::Graph srcGraph(&ctx, true); + auto err = ::zeGraphDumpContentsExp(&srcGraph, "dump", nullptr); + EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, err); +} + TEST(GraphTestApiSubmit, GivenNonNullPNextThenGraphAppendReturnsError) { GraphsCleanupGuard graphCleanup; Mock ctx; diff --git a/level_zero/core/test/unit_tests/experimental/test_graph_exporter.cpp b/level_zero/core/test/unit_tests/experimental/test_graph_exporter.cpp deleted file mode 100644 index 6a98dbdfae..0000000000 --- a/level_zero/core/test/unit_tests/experimental/test_graph_exporter.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (C) 2025 Intel Corporation - * - * SPDX-License-Identifier: MIT - * - */ - -#include "shared/test/common/helpers/variable_backup.h" -#include "shared/test/common/mocks/mock_io_functions.h" - -#include "level_zero/core/test/unit_tests/experimental/test_graph.h" - -#include "gtest/gtest.h" - -using namespace NEO; - -namespace L0 { -namespace ult { - -class MockGraphDotExporter : public GraphDotExporter { - public: - using GraphDotExporter::exportToString; - using GraphDotExporter::findSubgraphIndex; - using GraphDotExporter::findSubgraphIndexByCommandList; - using GraphDotExporter::generateNodeId; - using GraphDotExporter::generateSubgraphId; - using GraphDotExporter::getCommandNodeAttributes; - using GraphDotExporter::getCommandNodeLabel; - using GraphDotExporter::getSubgraphFillColor; - using GraphDotExporter::writeEdges; - using GraphDotExporter::writeForkJoinEdges; - using GraphDotExporter::writeHeader; - using GraphDotExporter::writeNodes; - using GraphDotExporter::writeSubgraphs; - using GraphDotExporter::writeUnjoinedForkEdges; -}; - -class GraphDotExporterTest : public ::testing::Test { - protected: - GraphsCleanupGuard graphCleanup; - Mock ctx; - MockGraphDotExporter exporter; - const std::string testFilePath = "test_graph_export.gv"; -}; - -TEST_F(GraphDotExporterTest, GivenNullFilePathWhenExportToFileThenReturnsInvalidArgument) { - Graph testGraph{&ctx, true}; - auto result = exporter.exportToFile(testGraph, nullptr); - EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, result); -} - -TEST_F(GraphDotExporterTest, GivenEmptyGraphWhenExportToStringThenContainsDigraphHeader) { - Graph testGraph{&ctx, true}; - - std::string dot = exporter.exportToString(testGraph); - EXPECT_NE(dot.find("digraph \"graph\" {"), std::string::npos); - EXPECT_NE(dot.find("rankdir=TB;"), std::string::npos); - EXPECT_NE(dot.find("nodesep=1;"), std::string::npos); - EXPECT_NE(dot.find("ranksep=1;"), std::string::npos); - EXPECT_NE(dot.find("node [shape=box, style=filled];"), std::string::npos); - EXPECT_NE(dot.find("edge [color=black];"), std::string::npos); - EXPECT_NE(dot.find('}'), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithSingleCommandWhenExportToStringThenContainsCommandNode) { - Graph testGraph{&ctx, true}; - Mock event; - Mock cmdlist; - - testGraph.capture(&cmdlist, &event, 0U, nullptr); - testGraph.stopCapturing(); - - std::string dot = exporter.exportToString(testGraph); - EXPECT_NE(dot.find("zeCommandListAppendBarrier"), std::string::npos); - EXPECT_NE(dot.find("L0_S0_C0"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithMultipleCommandsWhenExportToStringThenContainsSequentialEdges) { - Graph testGraph{&ctx, true}; - Mock event; - Mock cmdlist; - - testGraph.capture(&cmdlist, &event, 0U, nullptr); - testGraph.capture(&cmdlist, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - testGraph.stopCapturing(); - - std::string dot = exporter.exportToString(testGraph); - EXPECT_NE(dot.find("L0_S0_C0 -> L0_S0_C1"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, WhenWriteHeaderThenGeneratesValidDotHeader) { - Graph testGraph{&ctx, true}; - - std::ostringstream dot; - exporter.writeHeader(dot); - std::string header = dot.str(); - - EXPECT_NE(header.find("digraph \"graph\" {"), std::string::npos); - EXPECT_NE(header.find("rankdir=TB;"), std::string::npos); - EXPECT_NE(header.find("nodesep=1;"), std::string::npos); - EXPECT_NE(header.find("ranksep=1;"), std::string::npos); - EXPECT_NE(header.find("node [shape=box, style=filled];"), std::string::npos); - EXPECT_NE(header.find("edge [color=black];"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithCommandWhenWriteNodesThenGeneratesNodeDefinitions) { - Graph testGraph{&ctx, true}; - Mock event; - Mock cmdlist; - - testGraph.capture(&cmdlist, &event, 0U, nullptr); - testGraph.stopCapturing(); - - std::ostringstream dot; - exporter.writeNodes(dot, testGraph, 0, 0); - std::string output = dot.str(); - - EXPECT_NE(output.find("L0_S0_C0"), std::string::npos); - EXPECT_NE(output.find("zeCommandListAppendBarrier"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithMultipleCommandsWhenWriteEdgesThenGeneratesSequentialEdges) { - Graph testGraph{&ctx, true}; - Mock event; - Mock cmdlist; - - testGraph.capture(&cmdlist, &event, 0U, nullptr); - testGraph.capture(&cmdlist, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - testGraph.stopCapturing(); - - std::ostringstream dot; - exporter.writeEdges(dot, testGraph, 0, 0); - std::string output = dot.str(); - - EXPECT_NE(output.find("L0_S0_C0 -> L0_S0_C1"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithCommandWhenGetCommandNodeLabelThenReturnsCorrectLabel) { - Graph testGraph{&ctx, true}; - Mock event; - Mock cmdlist; - - testGraph.capture(&cmdlist, &event, 0U, nullptr); - testGraph.stopCapturing(); - - std::string label = exporter.getCommandNodeLabel(testGraph, 0); - EXPECT_EQ(label, "zeCommandListAppendBarrier"); -} - -TEST_F(GraphDotExporterTest, GivenDifferentCommandTypesWhenGetCommandNodeAttributesThenReturnsCorrectColors) { - Graph testGraph{&ctx, true}; - Mock event; - Mock cmdlist; - - testGraph.capture(&cmdlist, &event, 0U, nullptr); - testGraph.capture(&cmdlist, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - testGraph.capture(&cmdlist, &event); - - testGraph.stopCapturing(); - - EXPECT_EQ(exporter.getCommandNodeAttributes(testGraph, 0), ", fillcolor=orange"); - EXPECT_EQ(exporter.getCommandNodeAttributes(testGraph, 1), ", fillcolor=lightblue"); - EXPECT_EQ(exporter.getCommandNodeAttributes(testGraph, 2), ", fillcolor=yellow"); -} - -TEST_F(GraphDotExporterTest, WhenGenerateNodeIdThenReturnsCorrectFormat) { - EXPECT_EQ(exporter.generateNodeId(0, 0, 0), "L0_S0_C0"); - EXPECT_EQ(exporter.generateNodeId(1, 2, 3), "L1_S2_C3"); - EXPECT_EQ(exporter.generateNodeId(10, 20, 30), "L10_S20_C30"); -} - -TEST_F(GraphDotExporterTest, WhenGenerateSubgraphIdThenReturnsCorrectFormat) { - EXPECT_EQ(exporter.generateSubgraphId(0, 0), "L0_S0"); - EXPECT_EQ(exporter.generateSubgraphId(1, 2), "L1_S2"); - EXPECT_EQ(exporter.generateSubgraphId(10, 20), "L10_S20"); -} - -TEST_F(GraphDotExporterTest, WhenGetSubgraphFillColorThenReturnsCorrectColors) { - EXPECT_EQ(exporter.getSubgraphFillColor(1), "grey90"); - EXPECT_EQ(exporter.getSubgraphFillColor(2), "grey80"); - EXPECT_EQ(exporter.getSubgraphFillColor(3), "grey70"); - EXPECT_EQ(exporter.getSubgraphFillColor(4), "grey60"); - EXPECT_EQ(exporter.getSubgraphFillColor(5), "grey50"); -} - -TEST_F(GraphDotExporterTest, GivenDeepLevelWhenGetSubgraphFillColorThenReturnsDeepestColor) { - EXPECT_EQ(exporter.getSubgraphFillColor(6), "grey50"); - EXPECT_EQ(exporter.getSubgraphFillColor(10), "grey50"); - EXPECT_EQ(exporter.getSubgraphFillColor(100), "grey50"); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithoutSubgraphsWhenWriteSubgraphsThenGeneratesNoOutput) { - Graph testGraph{&ctx, true}; - std::ostringstream dot; - exporter.writeSubgraphs(dot, testGraph, 0); - std::string output = dot.str(); - - EXPECT_TRUE(output.empty()); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithSubgraphsWhenWriteSubgraphsThenGeneratesSubgraphStructure) { - Graph testGraph{&ctx, true}; - Mock forkEvent; - Mock joinEvent; - Mock mainCmdList; - Mock subCmdList; - - Graph *testGraphPtr = &testGraph; - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent, 0U, nullptr); - - Graph *subGraph = nullptr; - testGraph.forkTo(subCmdList, subGraph, forkEvent); - ASSERT_NE(subGraph, nullptr); - - captureCommand(subCmdList, subGraph, &subCmdList, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - captureCommand(subCmdList, subGraph, &subCmdList, &joinEvent, 0U, nullptr); - - testGraph.tryJoinOnNextCommand(subCmdList, joinEvent); - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, nullptr, 0U, nullptr); - testGraph.stopCapturing(); - - std::ostringstream dot; - exporter.writeSubgraphs(dot, testGraph, 0); - std::string output = dot.str(); - - EXPECT_NE(output.find("// Subgraphs:"), std::string::npos); - EXPECT_NE(output.find("subgraph cluster_L1_S0"), std::string::npos); - EXPECT_NE(output.find("label=\"Subgraph 1-0\""), std::string::npos); - EXPECT_NE(output.find("style=filled"), std::string::npos); - EXPECT_NE(output.find("fillcolor=grey90"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithNestedSubgraphsWhenWriteSubgraphsThenGeneratesNestedStructure) { - Graph testGraph{&ctx, true}; - Mock forkEvent1; - Mock forkEvent2; - Mock joinEvent1; - Mock joinEvent2; - Mock mainCmdList; - Mock subCmdList1; - Mock subCmdList2; - - Graph *testGraphPtr = &testGraph; - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent1, 0U, nullptr); - - Graph *subGraph1 = nullptr; - testGraph.forkTo(subCmdList1, subGraph1, forkEvent1); - ASSERT_NE(subGraph1, nullptr); - - captureCommand(subCmdList1, subGraph1, &subCmdList1, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - - Graph *subGraph2 = nullptr; - captureCommand(subCmdList1, subGraph1, &subCmdList1, &forkEvent2, 0U, nullptr); - subGraph1->forkTo(subCmdList2, subGraph2, forkEvent2); - ASSERT_NE(subGraph2, nullptr); - - captureCommand(subCmdList2, subGraph2, &subCmdList2, &joinEvent2, 0U, nullptr); - - subGraph1->tryJoinOnNextCommand(subCmdList2, joinEvent2); - captureCommand(subCmdList1, subGraph1, &subCmdList1, &joinEvent1, 0U, nullptr); - - testGraph.tryJoinOnNextCommand(subCmdList1, joinEvent1); - testGraph.stopCapturing(); - - std::ostringstream dot; - exporter.writeSubgraphs(dot, testGraph, 0); - std::string output = dot.str(); - - EXPECT_NE(output.find("subgraph cluster_L1_S0"), std::string::npos); - EXPECT_NE(output.find("subgraph cluster_L2_S0"), std::string::npos); - EXPECT_NE(output.find("fillcolor=grey90"), std::string::npos); - EXPECT_NE(output.find("fillcolor=grey80"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithAdjacentSubgraphsWhenWriteSubgraphsThenGeneratesMultipleSubgraphs) { - Graph testGraph{&ctx, true}; - Mock forkEvent1; - Mock forkEvent2; - Mock joinEvent1; - Mock joinEvent2; - Mock mainCmdList; - Mock subCmdList1; - Mock subCmdList2; - - Graph *testGraphPtr = &testGraph; - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent1, 0U, nullptr); - - Graph *subGraph1 = nullptr; - testGraph.forkTo(subCmdList1, subGraph1, forkEvent1); - ASSERT_NE(subGraph1, nullptr); - - captureCommand(subCmdList1, subGraph1, &subCmdList1, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - captureCommand(subCmdList1, subGraph1, &subCmdList1, &joinEvent1, 0U, nullptr); - - testGraph.tryJoinOnNextCommand(subCmdList1, joinEvent1); - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent2, 0U, nullptr); - - Graph *subGraph2 = nullptr; - testGraph.forkTo(subCmdList2, subGraph2, forkEvent2); - ASSERT_NE(subGraph2, nullptr); - - captureCommand(subCmdList2, subGraph2, &subCmdList2, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - captureCommand(subCmdList2, subGraph2, &subCmdList2, &joinEvent2, 0U, nullptr); - - testGraph.tryJoinOnNextCommand(subCmdList2, joinEvent2); - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, nullptr, 0U, nullptr); - testGraph.stopCapturing(); - - std::ostringstream dot; - exporter.writeSubgraphs(dot, testGraph, 0); - std::string output = dot.str(); - - EXPECT_NE(output.find("// Subgraphs:"), std::string::npos); - EXPECT_NE(output.find("subgraph cluster_L1_S0"), std::string::npos); - EXPECT_NE(output.find("subgraph cluster_L1_S1"), std::string::npos); - EXPECT_NE(output.find("label=\"Subgraph 1-0\""), std::string::npos); - EXPECT_NE(output.find("label=\"Subgraph 1-1\""), std::string::npos); -} - -TEST_F(GraphDotExporterTest, WhenFindSubgraphIndexWithInvalidSubgraphThenReturnsNullopt) { - const StackVec subGraphs; - Graph fakeSubgraph{&ctx, true}; - auto index = exporter.findSubgraphIndex(subGraphs, &fakeSubgraph); - - EXPECT_FALSE(index.has_value()); -} - -TEST_F(GraphDotExporterTest, WhenFindSubgraphIndexWithValidGraphThenReturnsCorrectIndex) { - Graph testGraph{&ctx, true}; - Mock forkEvent; - Mock joinEvent; - Mock mainCmdList; - Mock subCmdList; - - Graph *testGraphPtr = &testGraph; - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent, 0U, nullptr); - - Graph *subGraph = nullptr; - testGraph.forkTo(subCmdList, subGraph, forkEvent); - ASSERT_NE(subGraph, nullptr); - - testGraph.tryJoinOnNextCommand(subCmdList, joinEvent); - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, nullptr, 0U, nullptr); - testGraph.stopCapturing(); - - const auto &subGraphs = testGraph.getSubgraphs(); - auto index = exporter.findSubgraphIndex(subGraphs, subGraph); - - ASSERT_TRUE(index.has_value()); - EXPECT_EQ(index.value(), 0U); -} - -TEST_F(GraphDotExporterTest, WhenFindSubgraphIndexByCommandListWithInvalidCommandListThenReturnsNullopt) { - const StackVec subGraphs; - Mock fakeCmdList; - auto index = exporter.findSubgraphIndexByCommandList(subGraphs, &fakeCmdList); - - EXPECT_FALSE(index.has_value()); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithEmptySubgraphWhenWriteForkJoinEdgesThenNoEdges) { - Graph testGraph{&ctx, true}; - Mock forkEvent; - Mock joinEvent; - Mock mainCmdList; - Mock subCmdList; - - Graph *testGraphPtr = &testGraph; - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent, 0U, nullptr); - - Graph *subGraph = nullptr; - testGraph.forkTo(subCmdList, subGraph, forkEvent); - ASSERT_NE(subGraph, nullptr); - - testGraph.tryJoinOnNextCommand(subCmdList, joinEvent); - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, nullptr, 0U, nullptr); - testGraph.stopCapturing(); - - std::ostringstream dot; - exporter.writeForkJoinEdges(dot, testGraph, 0, 0); - std::string output = dot.str(); - - EXPECT_EQ(output.find("->"), std::string::npos); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithUnjoinedForksWhenWriteUnjoinedForkEdgesThenGeneratesUnjoinedEdges) { - Graph testGraph{&ctx, true}; - Mock forkEvent; - Mock mainCmdList; - Mock subCmdList; - - Graph *testGraphPtr = &testGraph; - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent, 0U, nullptr); - - Graph *subGraph = nullptr; - testGraph.forkTo(subCmdList, subGraph, forkEvent); - ASSERT_NE(subGraph, nullptr); - - captureCommand(subCmdList, subGraph, &subCmdList, nullptr, nullptr, 0U, nullptr, 0U, nullptr); - - std::ostringstream dot; - exporter.writeUnjoinedForkEdges(dot, testGraph, 0, 0); - std::string output = dot.str(); - - EXPECT_NE(output.find("// Unjoined forks:"), std::string::npos); - EXPECT_NE(output.find("L0_S0_C0 -> L1_S0_C0 [color=red, label=\"unjoined fork\"];"), std::string::npos); - - // Prevent double free with unjoined forks - Mock joinEvent; - testGraph.tryJoinOnNextCommand(subCmdList, joinEvent); - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent, 0U, nullptr); - testGraph.stopCapturing(); -} - -TEST_F(GraphDotExporterTest, GivenGraphWithEmptyUnjoinedSubgraphWhenWriteUnjoinedForkEdgesThenOutputIsEmpty) { - Graph testGraph{&ctx, true}; - Mock forkEvent; - Mock mainCmdList; - Mock subCmdList; - - Graph *testGraphPtr = &testGraph; - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent, 0U, nullptr); - - Graph *subGraph = nullptr; - testGraph.forkTo(subCmdList, subGraph, forkEvent); - ASSERT_NE(subGraph, nullptr); - - std::ostringstream dot; - exporter.writeUnjoinedForkEdges(dot, testGraph, 0, 0); - std::string output = dot.str(); - - EXPECT_EQ(output.find("L0_S0_C0 -> L1_S0_C0 [color=red, label=\"unjoined fork\"];"), std::string::npos); - - // Prevent double free with unjoined forks - Mock joinEvent; - testGraph.tryJoinOnNextCommand(subCmdList, joinEvent); - captureCommand(mainCmdList, testGraphPtr, &mainCmdList, &forkEvent, 0U, nullptr); - testGraph.stopCapturing(); -} - -class GraphDotExporterFileTest : public GraphDotExporterTest { - protected: - void SetUp() override { - GraphDotExporterTest::SetUp(); - - fopenBackup = std::make_unique>(&NEO::IoFunctions::fopenPtr, NEO::IoFunctions::mockFopen); - fwriteBackup = std::make_unique>(&NEO::IoFunctions::fwritePtr, NEO::IoFunctions::mockFwrite); - fcloseBackup = std::make_unique>(&NEO::IoFunctions::fclosePtr, NEO::IoFunctions::mockFclose); - - mockFopenReturnedBackup = std::make_unique>(&IoFunctions::mockFopenReturned); - - mockFopenCalledBefore = NEO::IoFunctions::mockFopenCalled; - mockFwriteCalledBefore = NEO::IoFunctions::mockFwriteCalled; - mockFcloseCalledBefore = NEO::IoFunctions::mockFcloseCalled; - } - - void setupSuccessfulWrite(Graph &testGraph) { - std::string expectedContent = exporter.exportToString(testGraph); - ASSERT_NE(expectedContent.size(), 0U); - - mockFwriteReturnBackup = std::make_unique>(&NEO::IoFunctions::mockFwriteReturn, expectedContent.size()); - - if (expectedContent.size() > 0) { - buffer = std::make_unique(expectedContent.size() + 1); - memset(buffer.get(), 0, expectedContent.size() + 1); - mockFwriteBufferBackup = std::make_unique>(&NEO::IoFunctions::mockFwriteBuffer, buffer.get()); - } - } - - void setupFailedOpen() { - *mockFopenReturnedBackup = static_cast(nullptr); - } - - void setupFailedWrite() { - mockFwriteReturnBackup = std::make_unique>(&NEO::IoFunctions::mockFwriteReturn, static_cast(0)); - } - - std::string getWrittenContent() const { - return buffer ? std::string(buffer.get()) : std::string{}; - } - - std::unique_ptr> fopenBackup; - std::unique_ptr> fwriteBackup; - std::unique_ptr> fcloseBackup; - std::unique_ptr> mockFopenReturnedBackup; - std::unique_ptr> mockFwriteReturnBackup; - std::unique_ptr> mockFwriteBufferBackup; - std::unique_ptr buffer; - - uint32_t mockFopenCalledBefore; - uint32_t mockFwriteCalledBefore; - uint32_t mockFcloseCalledBefore; -}; - -TEST_F(GraphDotExporterFileTest, GivenEmptyGraphWhenExportToFileThenWritesValidDotContent) { - Graph testGraph{&ctx, true}; - setupSuccessfulWrite(testGraph); - - auto result = exporter.exportToFile(testGraph, testFilePath.c_str()); - EXPECT_EQ(ZE_RESULT_SUCCESS, result); - - std::string writtenContent = getWrittenContent(); - EXPECT_NE(writtenContent.find("digraph \"graph\" {"), std::string::npos); - EXPECT_NE(writtenContent.find('}'), std::string::npos); - - EXPECT_EQ(mockFopenCalledBefore + 1, NEO::IoFunctions::mockFopenCalled); - EXPECT_EQ(mockFwriteCalledBefore + 1, NEO::IoFunctions::mockFwriteCalled); - EXPECT_EQ(mockFcloseCalledBefore + 1, NEO::IoFunctions::mockFcloseCalled); -} - -TEST_F(GraphDotExporterFileTest, GivenFailedFileOpenWhenExportToFileThenReturnsUnknownError) { - Graph testGraph{&ctx, true}; - setupFailedOpen(); - - auto result = exporter.exportToFile(testGraph, testFilePath.c_str()); - EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); - - EXPECT_EQ(mockFopenCalledBefore + 1, NEO::IoFunctions::mockFopenCalled); - EXPECT_EQ(mockFwriteCalledBefore, NEO::IoFunctions::mockFwriteCalled); -} - -TEST_F(GraphDotExporterFileTest, GivenFailedFileWriteWhenExportToFileThenReturnsUnknownError) { - Graph testGraph{&ctx, true}; - setupFailedWrite(); - - auto result = exporter.exportToFile(testGraph, testFilePath.c_str()); - EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); - - EXPECT_EQ(mockFopenCalledBefore + 1, NEO::IoFunctions::mockFopenCalled); - EXPECT_EQ(mockFwriteCalledBefore + 1, NEO::IoFunctions::mockFwriteCalled); - EXPECT_EQ(mockFcloseCalledBefore + 1, NEO::IoFunctions::mockFcloseCalled); -} - -} // namespace ult -} // namespace L0 diff --git a/level_zero/experimental/source/graph/graph.cpp b/level_zero/experimental/source/graph/graph.cpp index 6da1f8035d..ebb3c4cad8 100644 --- a/level_zero/experimental/source/graph/graph.cpp +++ b/level_zero/experimental/source/graph/graph.cpp @@ -7,17 +7,11 @@ #include "level_zero/experimental/source/graph/graph.h" -#include "shared/source/utilities/io_functions.h" - #include "level_zero/core/source/cmdlist/cmdlist.h" #include "level_zero/core/source/context/context.h" #include "level_zero/core/source/event/event.h" #include "level_zero/core/source/kernel/kernel_imp.h" -#include -#include -#include - namespace L0 { Graph::~Graph() { @@ -400,256 +394,4 @@ void recordHandleSignalEventFromPreviousCommand(L0::CommandList &srcCmdList, Gra captureTarget.registerSignallingEventFromPreviousCommand(*L0::Event::fromHandle(event)); } -ze_result_t GraphDotExporter::exportToFile(const Graph &graph, const char *filePath) const { - if (nullptr == filePath) { - return ZE_RESULT_ERROR_INVALID_ARGUMENT; - } - - FILE *file = NEO::IoFunctions::fopenPtr(filePath, "w"); - if (nullptr == file) { - return ZE_RESULT_ERROR_UNKNOWN; - } - - std::string dotContent = exportToString(graph); - size_t bytesWritten = NEO::IoFunctions::fwritePtr(dotContent.c_str(), 1, dotContent.size(), file); - NEO::IoFunctions::fclosePtr(file); - - if (bytesWritten != dotContent.size()) { - return ZE_RESULT_ERROR_UNKNOWN; - } - - return ZE_RESULT_SUCCESS; -} - -std::string GraphDotExporter::exportToString(const Graph &graph) const { - std::ostringstream dot; - - writeHeader(dot); - writeNodes(dot, graph, 0, 0); - writeEdges(dot, graph, 0, 0); - writeSubgraphs(dot, graph, 0); - - dot << "}\n"; - return dot.str(); -} - -void GraphDotExporter::writeHeader(std::ostringstream &dot) const { - dot << "digraph \"graph\" {\n"; - dot << " rankdir=TB;\n"; - dot << " nodesep=1;\n"; - dot << " ranksep=1;\n"; - dot << " node [shape=box, style=filled];\n"; - dot << " edge [color=black];\n\n"; -} - -void GraphDotExporter::writeNodes(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const { - const std::string indent(static_cast(level + 1) * 2, ' '); - dot << indent << "// Command nodes:\n"; - - const auto &commands = graph.getCapturedCommands(); - for (CapturedCommandId cmdId = 0; cmdId < static_cast(commands.size()); ++cmdId) { - const std::string nodeId = generateNodeId(level, subgraphId, cmdId); - const std::string label = getCommandNodeLabel(graph, cmdId); - const std::string attributes = getCommandNodeAttributes(graph, cmdId); - - dot << indent << nodeId << " [label=\"" << label << "\"" << attributes << "];\n"; - } - dot << "\n"; -} - -void GraphDotExporter::writeEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const { - writeSequentialEdges(dot, graph, level, subgraphId); - writeForkJoinEdges(dot, graph, level, subgraphId); - writeUnjoinedForkEdges(dot, graph, level, subgraphId); - - dot << "\n"; -} - -void GraphDotExporter::writeSequentialEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const { - const std::string indent(static_cast(level + 1) * 2, ' '); - - const auto &commands = graph.getCapturedCommands(); - dot << indent << "// Sequential edges:\n"; - - for (CapturedCommandId cmdId = 1; cmdId < static_cast(commands.size()); ++cmdId) { - const std::string fromNode = generateNodeId(level, subgraphId, cmdId - 1); - const std::string toNode = generateNodeId(level, subgraphId, cmdId); - dot << indent << fromNode << " -> " << toNode << ";\n"; - } -} - -void GraphDotExporter::writeForkJoinEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const { - const std::string indent(static_cast(level + 1) * 2, ' '); - - const auto &joinedForks = graph.getJoinedForks(); - const auto &subGraphs = graph.getSubgraphs(); - - dot << "\n" - << indent << "// Fork/Join edges:\n"; - - for (const auto &[forkCmdId, forkJoinInfo] : joinedForks) { - const auto subgraphIndex = findSubgraphIndex(subGraphs, forkJoinInfo.forkDestiny); - if (subgraphIndex && !forkJoinInfo.forkDestiny->getCapturedCommands().empty()) { - const auto &subgraphCommands = forkJoinInfo.forkDestiny->getCapturedCommands(); - const std::string forkNode = generateNodeId(level, subgraphId, forkJoinInfo.forkCommandId); - const std::string subgraphFirstNode = generateNodeId(level + 1, *subgraphIndex, 0); - const std::string subgraphLastNode = generateNodeId(level + 1, *subgraphIndex, static_cast(subgraphCommands.size()) - 1); - const std::string joinNode = generateNodeId(level, subgraphId, forkJoinInfo.joinCommandId); - - dot << indent << forkNode << " -> " << subgraphFirstNode << ";\n"; - dot << indent << subgraphLastNode << " -> " << joinNode << ";\n"; - } - } -} - -void GraphDotExporter::writeUnjoinedForkEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const { - const std::string indent(static_cast(level + 1) * 2, ' '); - - const auto &unjoinedForks = graph.getUnjoinedForks(); - const auto &subGraphs = graph.getSubgraphs(); - - dot << "\n" - << indent << "// Unjoined forks:\n"; - - for (const auto &[cmdList, forkInfo] : unjoinedForks) { - const auto subgraphIndex = findSubgraphIndexByCommandList(subGraphs, cmdList); - if (subgraphIndex && !subGraphs[*subgraphIndex]->getCapturedCommands().empty()) { - const std::string forkNode = generateNodeId(level, subgraphId, forkInfo.forkCommandId); - const std::string subgraphFirstNode = generateNodeId(level + 1, *subgraphIndex, 0); - dot << indent << forkNode << " -> " << subgraphFirstNode << " [color=red, label=\"unjoined fork\"];\n"; - } - } -} - -std::optional GraphDotExporter::findSubgraphIndex(const StackVec &subGraphs, const Graph *targetGraph) const { - for (uint32_t i = 0; i < static_cast(subGraphs.size()); ++i) { - if (subGraphs[i] == targetGraph) { - return i; - } - } - return std::nullopt; -} - -std::optional GraphDotExporter::findSubgraphIndexByCommandList(const StackVec &subGraphs, const L0::CommandList *cmdList) const { - for (uint32_t i = 0; i < static_cast(subGraphs.size()); ++i) { - if (subGraphs[i]->getExecutionTarget() == cmdList) { - return i; - } - } - return std::nullopt; -} - -void GraphDotExporter::writeSubgraphs(std::ostringstream &dot, const Graph &graph, uint32_t level) const { - const auto &subGraphs = graph.getSubgraphs(); - if (subGraphs.empty()) { - return; - } - - const std::string indent(static_cast(level + 1) * 2, ' '); - dot << indent << "// Subgraphs:\n"; - - for (uint32_t subgraphId = 0; subgraphId < static_cast(subGraphs.size()); ++subgraphId) { - const std::string clusterName = "cluster_" + generateSubgraphId(level + 1, subgraphId); - - dot << indent << "subgraph " << clusterName << " {\n"; - dot << indent << " label=\"Subgraph " << (level + 1) << "-" << subgraphId << "\";\n"; - dot << indent << " style=filled;\n"; - dot << indent << " fillcolor=" << getSubgraphFillColor(level + 1) << ";\n\n"; - - writeNodes(dot, *subGraphs[subgraphId], level + 1, subgraphId); - writeEdges(dot, *subGraphs[subgraphId], level + 1, subgraphId); - writeSubgraphs(dot, *subGraphs[subgraphId], level + 1); - - dot << indent << " }\n\n"; - } -} - -std::string GraphDotExporter::getCommandNodeLabel(const Graph &graph, CapturedCommandId cmdId) const { - const auto &commands = graph.getCapturedCommands(); - const auto &cmd = commands[cmdId]; - - std::string baseLabel; - switch (static_cast(cmd.index())) { -#define RR_CAPTURED_API(X) \ - case CaptureApi::X: \ - baseLabel = #X; \ - break; - - RR_CAPTURED_APIS() -#undef RR_CAPTURED_API - - default: - baseLabel = "Unknown"; - break; - } - - return baseLabel; -} - -std::string GraphDotExporter::getCommandNodeAttributes(const Graph &graph, CapturedCommandId cmdId) const { - const auto &commands = graph.getCapturedCommands(); - const auto &cmd = commands[cmdId]; - - switch (static_cast(cmd.index())) { - case CaptureApi::zeCommandListAppendMemoryCopy: - case CaptureApi::zeCommandListAppendMemoryCopyRegion: - case CaptureApi::zeCommandListAppendMemoryCopyFromContext: - case CaptureApi::zeCommandListAppendMemoryFill: - return ", fillcolor=lightblue"; - - case CaptureApi::zeCommandListAppendBarrier: - case CaptureApi::zeCommandListAppendMemoryRangesBarrier: - return ", fillcolor=orange"; - - case CaptureApi::zeCommandListAppendSignalEvent: - case CaptureApi::zeCommandListAppendWaitOnEvents: - case CaptureApi::zeCommandListAppendEventReset: - return ", fillcolor=yellow"; - - case CaptureApi::zeCommandListAppendImageCopy: - case CaptureApi::zeCommandListAppendImageCopyRegion: - case CaptureApi::zeCommandListAppendImageCopyToMemory: - case CaptureApi::zeCommandListAppendImageCopyFromMemory: - case CaptureApi::zeCommandListAppendImageCopyToMemoryExt: - case CaptureApi::zeCommandListAppendImageCopyFromMemoryExt: - return ", fillcolor=lightgreen"; - - case CaptureApi::zeCommandListAppendWriteGlobalTimestamp: - case CaptureApi::zeCommandListAppendQueryKernelTimestamps: - return ", fillcolor=pink"; - - default: - return ", fillcolor=aliceblue"; - } -} - -std::string GraphDotExporter::generateNodeId(uint32_t level, uint32_t subgraphId, CapturedCommandId cmdId) const { - std::ostringstream oss; - oss << "L" << level << "_S" << subgraphId << "_C" << cmdId; - return oss.str(); -} - -std::string GraphDotExporter::generateSubgraphId(uint32_t level, uint32_t subgraphId) const { - std::ostringstream oss; - oss << "L" << level << "_S" << subgraphId; - return oss.str(); -} - -std::string GraphDotExporter::getSubgraphFillColor(uint32_t level) const { - const std::vector colors = { - "grey90", // Level 1 - "grey80", // Level 2 - "grey70", // Level 3 - "grey60", // Level 4 - "grey50" // Level 5+ - }; - - size_t colorIndex = static_cast(level) - 1; - if (colorIndex >= colors.size()) { - colorIndex = colors.size() - 1; - } - - return colors[colorIndex]; -} - } // namespace L0 \ No newline at end of file diff --git a/level_zero/experimental/source/graph/graph.h b/level_zero/experimental/source/graph/graph.h index 0e19f87dca..1792e51923 100644 --- a/level_zero/experimental/source/graph/graph.h +++ b/level_zero/experimental/source/graph/graph.h @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -52,22 +51,6 @@ using ClosureVariants = std::variant< using CapturedCommand = ClosureVariants; using CapturedCommandId = uint32_t; - -struct Graph; - -struct ForkInfo { - CapturedCommandId forkCommandId = 0; - ze_event_handle_t forkEvent = nullptr; -}; - -struct ForkJoinInfo { - CapturedCommandId forkCommandId = 0; - CapturedCommandId joinCommandId = 0; - ze_event_handle_t forkEvent = nullptr; - ze_event_handle_t joinEvent = nullptr; - Graph *forkDestiny = nullptr; -}; - struct Graph : _ze_graph_handle_t { Graph(L0::Context *ctx, bool preallocated) : ctx(ctx), preallocated(preallocated) { commands.reserve(16); @@ -102,22 +85,10 @@ struct Graph : _ze_graph_handle_t { return ZE_RESULT_SUCCESS; } - const std::vector &getCapturedCommands() const { + const std::vector &getCapturedCommands() { return commands; } - const StackVec &getSubgraphs() const { - return subGraphs; - } - - const std::unordered_map &getJoinedForks() const { - return joinedForks; - } - - const std::unordered_map &getUnjoinedForks() const { - return unjoinedForks; - } - Graph *getJoinedForkTarget(CapturedCommandId cmdId) { auto it = joinedForks.find(cmdId); if (joinedForks.end() == it) { @@ -194,7 +165,21 @@ struct Graph : _ze_graph_handle_t { bool wasCapturingStopped = false; std::unordered_map recordedSignals; + + struct ForkInfo { + CapturedCommandId forkCommandId = 0; + ze_event_handle_t forkEvent = nullptr; + }; + std::unordered_map unjoinedForks; + + struct ForkJoinInfo { + CapturedCommandId forkCommandId = 0; + CapturedCommandId joinCommandId = 0; + ze_event_handle_t forkEvent = nullptr; + ze_event_handle_t joinEvent = nullptr; + Graph *forkDestiny = nullptr; + }; std::unordered_map joinedForks; }; @@ -287,31 +272,6 @@ struct ExecutableGraph : _ze_executable_graph_handle_t { GraphSubmissionChain submissionChain; }; -class GraphDotExporter { - public: - ze_result_t exportToFile(const Graph &graph, const char *filePath) const; - - protected: - std::string exportToString(const Graph &graph) const; - - void writeHeader(std::ostringstream &dot) const; - void writeNodes(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const; - void writeSubgraphs(std::ostringstream &dot, const Graph &graph, uint32_t level) const; - void writeEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const; - void writeSequentialEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const; - void writeForkJoinEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const; - void writeUnjoinedForkEdges(std::ostringstream &dot, const Graph &graph, uint32_t level, uint32_t subgraphId) const; - - std::optional findSubgraphIndex(const StackVec &subGraphs, const Graph *targetGraph) const; - std::optional findSubgraphIndexByCommandList(const StackVec &subGraphs, const L0::CommandList *cmdList) const; - - std::string getCommandNodeLabel(const Graph &graph, CapturedCommandId cmdId) const; - std::string getCommandNodeAttributes(const Graph &graph, CapturedCommandId cmdId) const; - std::string generateNodeId(uint32_t level, uint32_t subgraphId, CapturedCommandId cmdId) const; - std::string generateSubgraphId(uint32_t level, uint32_t subgraphId) const; - std::string getSubgraphFillColor(uint32_t level) const; -}; - constexpr size_t maxVariantSize = 2 * 64; #define RR_CAPTURED_API(X) \ static_assert(sizeof(Closure) <= maxVariantSize, #X " is too big for common variant. Please export some of its state to ClosureExternalStorage"); diff --git a/shared/source/utilities/io_functions.h b/shared/source/utilities/io_functions.h index 4ab8cbb412..cc8792659f 100644 --- a/shared/source/utilities/io_functions.h +++ b/shared/source/utilities/io_functions.h @@ -30,7 +30,7 @@ using fseekFuncPtr = int (*)(FILE *, long int, int); using ftellFuncPtr = long int (*)(FILE *); using rewindFuncPtr = decltype(&rewind); using freadFuncPtr = size_t (*)(void *, size_t, size_t, FILE *); -using fwriteFuncPtr = size_t (*)(const void *, size_t, size_t, FILE *); +using fwriteFuncPtr = decltype(&fwrite); using fflushFuncPtr = decltype(&fflush); using mkdirFuncPtr = int (*)(const char *);