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 4 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
3 changes: 2 additions & 1 deletion backends/graphs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
set (GRAPHS_SRCS
graphs.cpp
controls.cpp
parser.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
117 changes: 116 additions & 1 deletion backends/graphs/graphs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ limitations under the License.
#include "frontends/p4/evaluator/evaluator.h"
#include "frontends/p4/frontend.h"

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

namespace graphs {

Expand Down Expand Up @@ -55,6 +55,116 @@ MidEnd::MidEnd(CompilerOptions& options) {
});
}

class Graphs::GraphAttributeSetter {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think that the main function for the backend and the Graph methods implementation belong in 2 different files; apart from that it looks ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created a new file "p4c-graphs.cpp" to accommodate main funcion.

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);
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 "";
}

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 "";
}

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 "";
}
}
};

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);
}

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);
}

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);
}
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;
}

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;
}

class Options : public CompilerOptions {
public:
cstring graphsDir{"."};
Expand All @@ -66,6 +176,7 @@ class Options : public CompilerOptions {
}
};


} // namespace graphs

int main(int argc, char *const argv[]) {
Expand Down Expand Up @@ -117,5 +228,9 @@ int main(int argc, char *const argv[]) {
graphs::ControlGraphs cgen(&midEnd.refMap, &midEnd.typeMap, options.graphsDir);
top->getMain()->apply(cgen);

LOG1("Generating parser graphs");
graphs::ParserGraphs pgen(&midEnd.refMap, &midEnd.typeMap, options.graphsDir);
top->getMain()->apply(pgen);

return ::errorCount() > 0;
}
Loading