Skip to content

Commit

Permalink
Improvement of p4c-graphs backend (#3268)
Browse files Browse the repository at this point in the history
* Extended Table-node processing. Added visiting of Key-node and Action-nodes.

* parsers -> rewritten, so they now use boost library for generation of graphs
graph_visitor -> class implementing functionality:
- to create fullGraph in 'dot' format from boost graphs of program blocks
- to create fullGraph in predefined 'json' format
- to output separate graphs for each program block in 'dot' format (old functionality)
  • Loading branch information
st22nestrel authored May 2, 2022
1 parent bab0301 commit c6bd86f
Show file tree
Hide file tree
Showing 19 changed files with 1,845 additions and 75 deletions.
2 changes: 2 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,14 @@ cc_library(
srcs = [
"backends/graphs/controls.cpp",
"backends/graphs/graphs.cpp",
"backends/graphs/graph_visitor.cpp",
"backends/graphs/p4c-graphs.cpp",
"backends/graphs/parsers.cpp",
],
hdrs = [
"backends/graphs/controls.h",
"backends/graphs/graphs.h",
"backends/graphs/graph_visitor.h",
"backends/graphs/parsers.h",
"backends/graphs/version.h",
],
Expand Down
2 changes: 2 additions & 0 deletions backends/graphs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ set (GRAPHS_SRCS
graphs.cpp
controls.cpp
parsers.cpp
graph_visitor.cpp
p4c-graphs.cpp
)

set (GRAPHS_HDRS
graphs.h
controls.h
parsers.h
graph_visitor.h
)

add_cpplint_files(${CMAKE_CURRENT_SOURCE_DIR} "${GRAPHS_SRCS};${GRAPHS_HDRS}")
Expand Down
54 changes: 49 additions & 5 deletions backends/graphs/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# 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 and parser blocks.
now it supports the generation of graphs for top-level control and parser blocks,
generation of fullGraph, which merges graphs for top-level program blocks and
json representation of fullGraph.

dot files can be processed by the `dot` command from
[Graphviz](http://graphviz.org), which can lay out the nodes and
Expand All @@ -23,6 +25,43 @@ cd out
dot <name>.dot -Tpng > <name>.png
```

For generation of dot fullGraph, use option `--fullGraph` and
for generation of fullGraph represented in json, use option `--jsonOut`.

## Format of json output

Output in json format is an object with fields:

- `info` (object)

- `name` - Filename of P4 program.

- `nodes` (array) - Contains objects, representing program blocks.

- `type` - Type of program block. Valid values are "parser" and "control".

- `name` - Name of program block.

- `nodes` (array) - Contains object, representing nodes of CFG.

- `node_nmb` - Index of given object in array nodes.

- `name` - Node label.

- `type` - Type of node.

- `type_enum` - Enum value, representing type of node in backend *p4c-graphs*.

- `transitions` (array) - Contains objects, representing transitions between nodes in `nodes`.

- `from` - Index to node in `nodes`, from which edge starts.

- `to` - Index to node in `nodes`, in which edge ends.

- `cond` - Edge label.

Objects which represent program blocks are ordered in `nodes`, in the order in which they are defined in the main declaration of P4 program.

## Example

Here is the graph generated for the ingress control block of the
Expand All @@ -31,13 +70,18 @@ test program:

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

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

![Flowlet switching egress graph](resources/flowlet_switching-bmv2.egress.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)
[here](http://bmv2.org/switch_egress.png).
Examples of generated fullGraph for [flowlet_switching-bmv2.p4](../../testdata/p4_16_samples/flowlet_switching-bmv2.p4) program
and [firewall.p4](https://github.com/p4lang/tutorials/blob/master/exercises/firewall/solution/firewall.p4) program
can be found in [resources](resources/), also with json fullGraph.
115 changes: 87 additions & 28 deletions backends/graphs/controls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ limitations under the License.
#include "frontends/p4/tableApply.h"
#include "lib/log.h"
#include "lib/nullstream.h"
#include "lib/path.h"
#include "controls.h"

namespace graphs {
Expand All @@ -33,11 +32,12 @@ using Graph = ControlGraphs::Graph;

Graph *ControlGraphs::ControlStack::pushBack(Graph &currentSubgraph, const cstring &name) {
auto &newSubgraph = currentSubgraph.create_subgraph();
boost::get_property(newSubgraph, boost::graph_name) = "cluster" + getName(name);
boost::get_property(newSubgraph, boost::graph_graph_attribute)["label"] = getName(name);
// Justify the subgraph label to the right as it usually makes the generated
// graph more readable than the default (center).
boost::get_property(newSubgraph, boost::graph_graph_attribute)["labeljust"] = "r";
auto fullName = getName(name);
boost::get_property(newSubgraph, boost::graph_name) = "cluster" + fullName;
boost::get_property(newSubgraph, boost::graph_graph_attribute)["label"] =
boost::get_property(currentSubgraph, boost::graph_name) +
(fullName != "" ? "." + fullName : fullName);
boost::get_property(newSubgraph, boost::graph_graph_attribute)["fontsize"] = "22pt";
boost::get_property(newSubgraph, boost::graph_graph_attribute)["style"] = "bold";
names.push_back(name);
subgraphs.push_back(&newSubgraph);
Expand Down Expand Up @@ -75,40 +75,30 @@ ControlGraphs::ControlGraphs(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
visitDagOnce = false;
}

void ControlGraphs::writeGraphToFile(const Graph &g, const cstring &name) {
auto path = Util::PathName(graphsDir).join(name + ".dot");
auto out = openFile(path.toString(), false);
if (out == nullptr) {
::error(ErrorType::ERR_IO, "Failed to open file %1%", path.toString());
return;
}
// custom label writers not supported with subgraphs, so we populate
// *_attribute_t properties instead using our GraphAttributeSetter class.
boost::write_graphviz(*out, g);
}

bool ControlGraphs::preorder(const IR::PackageBlock *block) {
for (auto it : block->constantValue) {
if (!it.second) continue;
if (it.second->is<IR::ControlBlock>()) {
auto name = it.second->to<IR::ControlBlock>()->container->name;
LOG1("Generating graph for top-level control " << name);
Graph g_;
g = &g_;
BUG_CHECK(controlStack.isEmpty(), "Invalid control stack state");
g = controlStack.pushBack(g_, "");

Graph *g_ = new Graph();
g = g_;
instanceName = boost::none;
boost::get_property(g_, boost::graph_name) = name;
boost::get_property(*g_, boost::graph_name) = name;
BUG_CHECK(controlStack.isEmpty(), "Invalid control stack state");
g = controlStack.pushBack(*g_, "");
start_v = add_vertex("__START__", VertexType::OTHER);
exit_v = add_vertex("__EXIT__", VertexType::OTHER);
parents = {{start_v, new EdgeUnconditional()}};
visit(it.second->getNode());
for (auto parent : parents)

for (auto parent : parents) {
add_edge(parent.first, exit_v, parent.second->label());
BUG_CHECK(g_.is_root(), "Invalid graph");
}
BUG_CHECK((*g_).is_root(), "Invalid graph");
controlStack.popBack();
GraphAttributeSetter()(g_);
writeGraphToFile(g_, name);
controlGraphsArray.push_back(g_);
} else if (it.second->is<IR::PackageBlock>()) {
visit(it.second->getNode());
}
Expand All @@ -131,6 +121,7 @@ bool ControlGraphs::preorder(const IR::P4Control *cont) {
return_parents.clear();
visit(cont->body);
merge_other_statements_into_vertex();

parents.insert(parents.end(), return_parents.begin(), return_parents.end());
return_parents.clear();
if (doPop) g = controlStack.popBack();
Expand All @@ -141,17 +132,20 @@ bool ControlGraphs::preorder(const IR::BlockStatement *statement) {
for (const auto component : statement->components)
visit(component);
merge_other_statements_into_vertex();

return false;
}

bool ControlGraphs::preorder(const IR::IfStatement *statement) {
std::stringstream sstream;
statement->condition->dbprint(sstream);
auto v = add_and_connect_vertex(cstring(sstream), VertexType::CONDITION);

Parents new_parents;
parents = {{v, new EdgeIf(EdgeIf::Branch::TRUE)}};
visit(statement->ifTrue);
merge_other_statements_into_vertex();

new_parents.insert(new_parents.end(), parents.begin(), parents.end());
parents = {{v, new EdgeIf(EdgeIf::Branch::FALSE)}};
if (statement->ifFalse != nullptr) {
Expand Down Expand Up @@ -207,6 +201,7 @@ bool ControlGraphs::preorder(const IR::SwitchStatement *statement) {

bool ControlGraphs::preorder(const IR::MethodCallStatement *statement) {
auto instance = P4::MethodInstance::resolve(statement->methodCall, refMap, typeMap);

if (instance->is<P4::ApplyMethod>()) {
auto am = instance->to<P4::ApplyMethod>();
if (am->object->is<IR::P4Table>()) {
Expand Down Expand Up @@ -244,23 +239,87 @@ bool ControlGraphs::preorder(const IR::AssignmentStatement *statement) {

bool ControlGraphs::preorder(const IR::ReturnStatement *) {
merge_other_statements_into_vertex();

return_parents.insert(return_parents.end(), parents.begin(), parents.end());
parents.clear();
return false;
}

bool ControlGraphs::preorder(const IR::ExitStatement *) {
merge_other_statements_into_vertex();

for (auto parent : parents)
add_edge(parent.first, exit_v, parent.second->label());
parents.clear();
return false;
}

bool ControlGraphs::preorder(const IR::Key *key) {
std::stringstream sstream;

// Build key
for (auto elVec : key->keyElements) {
sstream << elVec->matchType->path->name.name << ": ";
sstream << elVec->annotations->annotations.front()->expr.front() << "\\n";
}

auto v = add_and_connect_vertex(cstring(sstream), VertexType::KEY);

parents = {{v, new EdgeUnconditional()}};

return false;
}

bool ControlGraphs::preorder(const IR::P4Action *action) {
visit(action->body);
return false;
}

bool ControlGraphs::preorder(const IR::P4Table *table) {
auto name = controlStack.getName(table->controlPlaneName());
auto name = table->getName();

auto v = add_and_connect_vertex(name, VertexType::TABLE);

parents = {{v, new EdgeUnconditional()}};

auto key = table->getKey();
visit(key);

Parents keyNode;
keyNode.emplace_back(parents.back());

Parents new_parents;

auto actions = table->getActionList()->actionList;
for (auto action : actions){
parents = keyNode;

auto v = add_and_connect_vertex(action->getName(), VertexType::ACTION);

parents = {{v, new EdgeUnconditional()}};

if (action->expression->is<IR::MethodCallExpression>()) {
auto mce = action->expression->to<IR::MethodCallExpression>();

// needed for visiting body of P4Action
auto resolved = P4::MethodInstance::resolve(mce, refMap, typeMap);

if (resolved->is<P4::ActionCall>()) {
auto ac = resolved->to<P4::ActionCall>();
if (ac->action->is<IR::P4Action>()) {
visit(ac->action->to<IR::P4Action>());
}
}
}

merge_other_statements_into_vertex();

new_parents.insert(new_parents.end(), parents.begin(), parents.end());
parents.clear();
}

parents = new_parents;

return false;
}

Expand Down
4 changes: 3 additions & 1 deletion backends/graphs/controls.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ class ControlGraphs : public Graphs {
bool preorder(const IR::ReturnStatement *) override;
bool preorder(const IR::ExitStatement *) override;
bool preorder(const IR::P4Table *table) override;
bool preorder(const IR::Key *key) override;
bool preorder(const IR::P4Action *action) override;

void writeGraphToFile(const Graph &g, const cstring &name);
std::vector<Graph *> controlGraphsArray{};

private:
P4::ReferenceMap *refMap; P4::TypeMap *typeMap;
Expand Down
Loading

0 comments on commit c6bd86f

Please sign in to comment.