Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser graph generation support for graphs backend #969

Merged
merged 5 commits into from
Oct 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion backends/graphs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
set (GRAPHS_SRCS
graphs.cpp
controls.cpp
parser.cpp
p4c-graphs.cpp
)

set (GRAPHS_HDRS
controls.h
graphs.h
)

add_cpplint_files(${CMAKE_CURRENT_SOURCE_DIR} "${GRAPHS_SRCS};${GRAPHS_HDRS}")
Expand Down
8 changes: 7 additions & 1 deletion backends/graphs/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Graphs Backend

This backend produces visual representations of a P4 program as dot files. For
now it only supports the generation of graphs for top-level control blocks.
now it only supports the generation of graphs for top-level control and parser blocks.

## Dependencies

Expand All @@ -26,6 +26,12 @@ test program:

![Flowlet switching ingress graph](resources/flowlet_switching-bmv2.ingress.png)

Here is the graph generated for the parser block of the
[flowlet_switching-bmv2.p4](../../testdata/p4_16_samples/flowlet_switching-bmv2.p4)
test program:

![Flowlet switching ingress graph](resources/flowlet_switching-bmv2.parser.png)

For a more complex example, you can look at the control graph generated for the
egress control of
[switch.p4](https://github.com/p4lang/switch/tree/f219b4f4e25c2db581f3b91c8da94a7c3ac701a7/p4src)
Expand Down
127 changes: 2 additions & 125 deletions backends/graphs/controls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

#include "controls.h"

#include <boost/graph/graphviz.hpp>

#include <iostream>

#include "graphs.h"

#include "frontends/p4/methodInstance.h"
#include "frontends/p4/tableApply.h"
#include "lib/log.h"
Expand All @@ -28,45 +28,6 @@ limitations under the License.

namespace graphs {

class EdgeUnconditional : public EdgeTypeIface {
public:
EdgeUnconditional() = default;
cstring label() const override { return ""; };
};

class EdgeIf : public EdgeTypeIface {
public:
enum class Branch { TRUE, FALSE };
explicit EdgeIf(Branch branch) : branch(branch) { }
cstring label() const override {
switch (branch) {
case Branch::TRUE:
return "TRUE";
case Branch::FALSE:
return "FALSE";
}
BUG("unreachable");
return "";
};

private:
Branch branch;
};

class EdgeSwitch : public EdgeTypeIface {
public:
explicit EdgeSwitch(const IR::Expression *labelExpr)
: labelExpr(labelExpr) { }
cstring label() const override {
std::stringstream sstream;
labelExpr->dbprint(sstream);
return cstring(sstream);
};

private:
const IR::Expression *labelExpr;
};

using Graph = ControlGraphs::Graph;

Graph *ControlGraphs::ControlStack::pushBack(Graph &currentSubgraph, const cstring &name) {
Expand Down Expand Up @@ -113,90 +74,6 @@ ControlGraphs::ControlGraphs(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
visitDagOnce = false;
}

vertex_t ControlGraphs::add_vertex(const cstring &name, VertexType type) {
auto v = boost::add_vertex(*g);
boost::put(&Vertex::name, *g, v, name);
boost::put(&Vertex::type, *g, v, type);
return g->local_to_global(v);
}

void ControlGraphs::add_edge(const vertex_t &from, const vertex_t &to, const cstring &name) {
auto ep = boost::add_edge(from, to, g->root());
boost::put(boost::edge_name, g->root(), ep.first, name);
}

boost::optional<vertex_t> ControlGraphs::merge_other_statements_into_vertex() {
if (statementsStack.empty()) return boost::none;
std::stringstream sstream;
if (statementsStack.size() == 1) {
statementsStack[0]->dbprint(sstream);
} else if (statementsStack.size() == 2) {
statementsStack[0]->dbprint(sstream);
sstream << "\n";
statementsStack[1]->dbprint(sstream);
} else {
statementsStack[0]->dbprint(sstream);
sstream << "\n...\n";
statementsStack.back()->dbprint(sstream);
}
auto v = add_vertex(cstring(sstream), VertexType::STATEMENTS);
for (auto parent : parents)
add_edge(parent.first, v, parent.second->label());
parents = {{v, new EdgeUnconditional()}};
statementsStack.clear();
return v;
}

vertex_t ControlGraphs::add_and_connect_vertex(const cstring &name, VertexType type) {
merge_other_statements_into_vertex();
auto v = add_vertex(name, type);
for (auto parent : parents)
add_edge(parent.first, v, parent.second->label());
return v;
}

class ControlGraphs::GraphAttributeSetter {
public:
void operator()(Graph &g) const {
auto vertices = boost::vertices(g);
for (auto vit = vertices.first; vit != vertices.second; ++vit) {
const auto &vinfo = g[*vit];
auto attrs = boost::get(boost::vertex_attribute, g);
attrs[*vit]["label"] = vinfo.name;
attrs[*vit]["style"] = vertexTypeGetStyle(vinfo.type);
attrs[*vit]["shape"] = vertexTypeGetShape(vinfo.type);
}
auto edges = boost::edges(g);
for (auto eit = edges.first; eit != edges.second; ++eit) {
auto attrs = boost::get(boost::edge_attribute, g);
attrs[*eit]["label"] = boost::get(boost::edge_name, g, *eit);
}
}

private:
static cstring vertexTypeGetShape(VertexType type) {
switch (type) {
case VertexType::TABLE:
return "ellipse";
default:
return "rectangle";
}
BUG("unreachable");
return "";
}

static cstring vertexTypeGetStyle(VertexType type) {
switch (type) {
case VertexType::CONTROL:
return "dashed";
default:
return "solid";
}
BUG("unreachable");
return "";
}
};

void ControlGraphs::writeGraphToFile(const Graph &g, const cstring &name) {
auto path = Util::PathName(graphsDir).join(name + ".dot");
auto out = openFile(path.toString(), false);
Expand Down
186 changes: 102 additions & 84 deletions backends/graphs/graphs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,108 +14,126 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

#include "ir/ir.h"
#include "ir/json_loader.h"
#include "lib/log.h"
#include "lib/error.h"
#include "lib/exceptions.h"
#include "lib/gc.h"
#include "lib/crash.h"
#include "lib/nullstream.h"
#include "frontends/common/parseInput.h"
#include "frontends/p4/evaluator/evaluator.h"
#include "frontends/p4/frontend.h"

#include "controls.h"
#include "graphs.h"

namespace graphs {

class MidEnd : public PassManager {
class Graphs::GraphAttributeSetter {
public:
P4::ReferenceMap refMap;
P4::TypeMap typeMap;
IR::ToplevelBlock *toplevel = nullptr;

explicit MidEnd(CompilerOptions& options);
IR::ToplevelBlock* process(const IR::P4Program *&program) {
program = program->apply(*this);
return toplevel;
void operator()(Graph &g) const {
auto vertices = boost::vertices(g);
for (auto vit = vertices.first; vit != vertices.second; ++vit) {
const auto &vinfo = g[*vit];
auto attrs = boost::get(boost::vertex_attribute, g);
attrs[*vit]["label"] = vinfo.name;
attrs[*vit]["style"] = vertexTypeGetStyle(vinfo.type);
attrs[*vit]["shape"] = vertexTypeGetShape(vinfo.type);
attrs[*vit]["margin"] = vertexTypeGetMargin(vinfo.type);
}
auto edges = boost::edges(g);
for (auto eit = edges.first; eit != edges.second; ++eit) {
auto attrs = boost::get(boost::edge_attribute, g);
attrs[*eit]["label"] = boost::get(boost::edge_name, g, *eit);
}
}

private:
static cstring vertexTypeGetShape(VertexType type) {
switch (type) {
case VertexType::TABLE:
return "ellipse";
case VertexType::HEADER:
return "box";
case VertexType::START:
return "circle";
case VertexType::DEFAULT:
return "doublecircle";
default:
return "rectangle";
}
BUG("unreachable");
return "";
}
};

MidEnd::MidEnd(CompilerOptions& options) {
bool isv1 = options.langVersion == CompilerOptions::FrontendVersion::P4_14;
refMap.setIsV1(isv1);
auto evaluator = new P4::EvaluatorPass(&refMap, &typeMap);
setName("MidEnd");

addPasses({
evaluator,
new VisitFunctor([this, evaluator]() { toplevel = evaluator->getToplevelBlock(); }),
});
}
static cstring vertexTypeGetStyle(VertexType type) {
switch (type) {
case VertexType::CONTROL:
return "dashed";
case VertexType::HEADER:
return "solid";
case VertexType::START:
return "solid";
case VertexType::DEFAULT:
return "solid";
default:
return "solid";
}
BUG("unreachable");
return "";
}

class Options : public CompilerOptions {
public:
cstring graphsDir{"."};
Options() {
registerOption("--graphs-dir", "dir",
[this](const char* arg) { graphsDir = arg; return true; },
"Use this directory to dump graphs in dot format "
"(default is current working directory)\n");
}
static cstring vertexTypeGetMargin(VertexType type) {
switch (type) {
case VertexType::HEADER:
return "0.05,0";
case VertexType::START:
return "0,0";
case VertexType::DEFAULT:
return "0,0";
default:
return "";
}
}
};

} // namespace graphs

int main(int argc, char *const argv[]) {
setup_gc_logging();
setup_signals();

graphs::Options options;
options.langVersion = CompilerOptions::FrontendVersion::P4_16;
options.compilerVersion = "0.0.5";

if (options.process(argc, argv) != nullptr)
options.setInputFile();
if (::errorCount() > 0)
return 1;

auto hook = options.getDebugHook();
Graphs::vertex_t Graphs::add_vertex(const cstring &name, VertexType type) {
auto v = boost::add_vertex(*g);
boost::put(&Vertex::name, *g, v, name);
boost::put(&Vertex::type, *g, v, type);
return g->local_to_global(v);
}

auto program = P4::parseP4File(options);
if (program == nullptr || ::errorCount() > 0)
return 1;
void Graphs::add_edge(const vertex_t &from, const vertex_t &to, const cstring &name) {
auto ep = boost::add_edge(from, to, g->root());
boost::put(boost::edge_name, g->root(), ep.first, name);
}

try {
P4::FrontEnd fe;
fe.addDebugHook(hook);
program = fe.run(options, program);
} catch (const Util::P4CExceptionBase &bug) {
std::cerr << bug.what() << std::endl;
return 1;
boost::optional<Graphs::vertex_t> Graphs::merge_other_statements_into_vertex() {
if (statementsStack.empty()) return boost::none;
std::stringstream sstream;
if (statementsStack.size() == 1) {
statementsStack[0]->dbprint(sstream);
} else if (statementsStack.size() == 2) {
statementsStack[0]->dbprint(sstream);
sstream << "\n";
statementsStack[1]->dbprint(sstream);
} else {
statementsStack[0]->dbprint(sstream);
sstream << "\n...\n";
statementsStack.back()->dbprint(sstream);
}
if (program == nullptr || ::errorCount() > 0)
return 1;

graphs::MidEnd midEnd(options);
midEnd.addDebugHook(hook);
const IR::ToplevelBlock *top = nullptr;
try {
top = midEnd.process(program);
if (options.dumpJsonFile)
JSONGenerator(*openFile(options.dumpJsonFile, true)) << program << std::endl;
} catch (const Util::P4CExceptionBase &bug) {
std::cerr << bug.what() << std::endl;
return 1;
}
if (::errorCount() > 0)
return 1;

LOG1("Generating graphs under " << options.graphsDir);
LOG1("Generating control graphs");
graphs::ControlGraphs cgen(&midEnd.refMap, &midEnd.typeMap, options.graphsDir);
top->getMain()->apply(cgen);
auto v = add_vertex(cstring(sstream), VertexType::STATEMENTS);
for (auto parent : parents)
add_edge(parent.first, v, parent.second->label());
parents = {{v, new EdgeUnconditional()}};
statementsStack.clear();
return v;
}

return ::errorCount() > 0;
Graphs::vertex_t Graphs::add_and_connect_vertex(const cstring &name, VertexType type) {
merge_other_statements_into_vertex();
auto v = add_vertex(name, type);
for (auto parent : parents)
add_edge(parent.first, v, parent.second->label());
return v;
}

} // namespace graphs

Loading