opencv/modules/gapi/src/compiler/passes/dump_dot.cpp
Ruslan Garnov 3df83dce7e Merge pull request #14741 from rgarnov:gapi_fix_includes
Changed quotes to brackets when including public includes (#14741)
2019-06-14 19:27:19 +03:00

225 lines
7.3 KiB
C++

// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
#include "precomp.hpp"
#include <iostream> // cout
#include <sstream> // stringstream
#include <fstream> // ofstream
#include <map>
#include <ade/passes/check_cycles.hpp>
#include <opencv2/gapi/gproto.hpp>
#include "compiler/gmodel.hpp"
#include "compiler/gislandmodel.hpp"
#include "compiler/passes/passes.hpp"
namespace cv { namespace gimpl { namespace passes {
// TODO: FIXME: Ideally all this low-level stuff with accessing ADE APIs directly
// should be incapsulated somewhere into GModel, so here we'd operate not
// with raw nodes and edges, but with Operations and Data it produce/consume.
void dumpDot(const ade::Graph &g, std::ostream& os)
{
GModel::ConstGraph gr(g);
const std::unordered_map<cv::GShape, std::string> data_labels = {
{cv::GShape::GMAT, "GMat"},
{cv::GShape::GSCALAR, "GScalar"},
{cv::GShape::GARRAY, "GArray"},
};
auto format_op_label = [&gr](ade::NodeHandle nh) -> std::string {
std::stringstream ss;
const cv::GKernel k = gr.metadata(nh).get<Op>().k;
ss << k.name << "_" << nh;
return ss.str();
};
auto format_op = [&format_op_label](ade::NodeHandle nh) -> std::string {
return "\"" + format_op_label(nh) + "\"";
};
auto format_obj = [&gr, &data_labels](ade::NodeHandle nh) -> std::string {
std::stringstream ss;
const auto &data = gr.metadata(nh).get<Data>();
ss << data_labels.at(data.shape) << "_" << data.rc;
return ss.str();
};
auto format_log = [&gr](ade::NodeHandle nh, const std::string &obj_name) {
std::stringstream ss;
const auto &msgs = gr.metadata(nh).get<Journal>().messages;
ss << "xlabel=\"";
if (!obj_name.empty()) { ss << "*** " << obj_name << " ***:\n"; };
for (const auto &msg : msgs) { ss << msg << "\n"; }
ss << "\"";
return ss.str();
};
// FIXME:
// Unify with format_log
auto format_log_e = [&gr](ade::EdgeHandle nh) {
std::stringstream ss;
const auto &msgs = gr.metadata(nh).get<Journal>().messages;
for (const auto &msg : msgs) { ss << "\n" << msg; }
return ss.str();
};
auto sorted = gr.metadata().get<ade::passes::TopologicalSortData>();
os << "digraph GAPI_Computation {\n";
// Prior to dumping the graph itself, list Data and Op nodes individually
// and put type information in labels.
// Also prepare list of nodes in islands, if any
std::map<std::string, std::vector<std::string> > islands;
for (auto &nh : sorted.nodes())
{
const auto node_type = gr.metadata(nh).get<NodeType>().t;
if (NodeType::DATA == node_type)
{
const auto obj_data = gr.metadata(nh).get<Data>();
const auto obj_name = format_obj(nh);
os << obj_name << " [label=\"" << obj_name << "\n" << obj_data.meta << "\"";
if (gr.metadata(nh).contains<Journal>()) { os << ", " << format_log(nh, obj_name); }
os << " ]\n";
if (gr.metadata(nh).contains<Island>())
islands[gr.metadata(nh).get<Island>().island].push_back(obj_name);
}
else if (NodeType::OP == gr.metadata(nh).get<NodeType>().t)
{
const auto obj_name = format_op(nh);
const auto obj_name_label = format_op_label(nh);
os << obj_name << " [label=\"" << obj_name_label << "\"";
if (gr.metadata(nh).contains<Journal>()) { os << ", " << format_log(nh, obj_name_label); }
os << " ]\n";
if (gr.metadata(nh).contains<Island>())
islands[gr.metadata(nh).get<Island>().island].push_back(obj_name);
}
}
// Then, dump Islands (only nodes, operations and data, without links)
for (const auto &isl : islands)
{
os << "subgraph \"cluster " + isl.first << "\" {\n";
for(auto isl_node : isl.second) os << isl_node << ";\n";
os << "label=\"" << isl.first << "\";";
os << "}\n";
}
// Now dump the graph
for (auto &nh : sorted.nodes())
{
// FIXME: Alan Kay probably hates me.
switch (gr.metadata(nh).get<NodeType>().t)
{
case NodeType::DATA:
{
const auto obj_name = format_obj(nh);
for (const auto &eh : nh->outEdges())
{
os << obj_name << " -> " << format_op(eh->dstNode())
<< " [ label = \"in_port: "
<< gr.metadata(eh).get<Input>().port;
if (gr.metadata(eh).contains<Journal>()) { os << format_log_e(eh); }
os << "\" ] \n";
}
}
break;
case NodeType::OP:
{
for (const auto &eh : nh->outEdges())
{
os << format_op(nh) << " -> " << format_obj(eh->dstNode())
<< " [ label = \"out_port: "
<< gr.metadata(eh).get<Output>().port
<< " \" ]; \n";
}
}
break;
default: GAPI_Assert(false);
}
}
// And finally dump a GIslandModel (not connected with GModel directly,
// but projected in the same .dot file side-by-side)
auto pIG = gr.metadata().get<IslandModel>().model;
GIslandModel::Graph gim(*pIG);
for (auto nh : gim.nodes())
{
switch (gim.metadata(nh).get<NodeKind>().k)
{
case NodeKind::ISLAND:
{
const auto island = gim.metadata(nh).get<FusedIsland>().object;
const auto isl_name = "\"" + island->name() + "\"";
for (auto out_nh : nh->outNodes())
{
os << isl_name << " -> \"slot:"
<< format_obj(gim.metadata(out_nh).get<DataSlot>()
.original_data_node)
<< "\"\n";
}
}
break;
case NodeKind::SLOT:
{
const auto obj_name = format_obj(gim.metadata(nh).get<DataSlot>()
.original_data_node);
for (auto cons_nh : nh->outNodes())
{
os << "\"slot:" << obj_name << "\" -> \""
<< gim.metadata(cons_nh).get<FusedIsland>().object->name()
<< "\"\n";
}
}
break;
default:
GAPI_Assert(false);
break;
}
}
os << "}" << std::endl;
}
void dumpDot(ade::passes::PassContext &ctx, std::ostream& os)
{
dumpDot(ctx.graph, os);
}
void dumpDotStdout(ade::passes::PassContext &ctx)
{
dumpDot(ctx, std::cout);
}
void dumpDotToFile(ade::passes::PassContext &ctx, const std::string& dump_path)
{
std::ofstream dump_file(dump_path);
if (dump_file.is_open())
{
dumpDot(ctx, dump_file);
dump_file << std::endl;
}
}
void dumpGraph(ade::passes::PassContext &ctx, const std::string& dump_path)
{
dump_path.empty() ? dumpDotStdout(ctx) : dumpDotToFile(ctx, dump_path);
}
}}} // cv::gimpl::passes