P4C
The P4 Compiler
Loading...
Searching...
No Matches
dump_parser.h
1
19#ifndef BACKENDS_TOFINO_BF_P4C_PARDE_DUMP_PARSER_H_
20#define BACKENDS_TOFINO_BF_P4C_PARDE_DUMP_PARSER_H_
21
22#include <sys/stat.h>
23
24#include <fstream>
25
26#include <boost/algorithm/string.hpp>
27
28#include "bf-p4c/bf-p4c-options.h"
29#include "bf-p4c/ir/gress.h"
30#include "bf-p4c/parde/parser_info.h"
31#include "ir/dump.h"
32#include "lib/log.h"
33
34class DotDumper {
35 protected:
36 std::string filename;
37
38 // use this to override the default color for nodes and edges
39 // each group will be assign an unique color
40 std::vector<assoc::set<void *>> *color_groups = nullptr;
41
42 bool detail = true;
43
44 std::stringstream out;
45
46 std::vector<std::string> colors = {"brown", "coral", "crimson", "deeppink", "gold",
47 "red", "blue", "green", "cyan", "orchid"};
48
49 public:
50 explicit DotDumper(std::string filename, bool detail) : filename(filename), detail(detail) {}
51
52 DotDumper(std::string filename, std::vector<assoc::set<void *>> &color_groups, bool detail)
53 : filename(filename), color_groups(&color_groups), detail(detail) {}
54
55 protected:
56 static std::string escape(std::string s) {
57 boost::replace_all(s, "ingress::", "");
58 boost::replace_all(s, "egress::", "");
59 boost::replace_all(s, "$", "\\$");
60 boost::replace_all(s, ".", "\\.");
61 boost::replace_all(s, "->", "-~");
62 boost::replace_all(s, "<", "\\<");
63 boost::replace_all(s, ">", "\\>");
64 boost::replace_all(s, "-~", "->");
65 return s;
66 }
67
68 std::string cluster_name;
69
70 std::string to_label(std::string label, const void *what) {
71 std::stringstream ss;
72 ss << cluster_name << label << what;
73 return ss.str();
74 }
75
76 std::string to_label(std::string label) {
77 std::stringstream ss;
78 ss << cluster_name << label;
79 return ss.str();
80 }
81
82 std::string lookup_color(void *obj) {
83 if (color_groups) {
84 for (auto it = color_groups->begin(); it != color_groups->end(); it++) {
85 if ((*it).count(obj)) {
86 auto cid = std::distance(color_groups->begin(), it) % colors.size();
87 return colors.at(cid);
88 }
89 }
90 }
91 return "undef";
92 }
93
94 void dump(const IR::BFN::Transition *transition) {
95 auto c = lookup_color((void *)transition); // NOLINT
96 if (c != "undef") out << "color=" << c << " ";
97
98 if (detail) out << "label=\"" << transition << "\"";
99 }
100
101 std::string get_color(const IR::BFN::ParserState *state) {
102 auto c = lookup_color((void *)state); // NOLINT
103 if (c != "undef") return c;
104
105 return "cornsilk";
106 }
107
108 void dump(const IR::BFN::ParserState *state) {
109 out << to_label("State", state);
110 out << " [shape=record, style=\"filled,rounded\", ";
111 out << "fillcolor=" << get_color(state);
112 out << ", label=\"{";
113 out << state->name;
114
115 if (detail) {
116 out << ":\\l\\l";
117
118 for (auto stmt : state->statements) out << " " << stmt << "\\l";
119
120 if (state->statements.size()) out << "\\l";
121
122 for (auto select : state->selects) out << " " << select << "\\l";
123 }
124
125 out << "}\"";
126 out << "];" << std::endl;
127 }
128
129 void dump(const IR::BFN::ParserGraph &graph, gress_t gress) {
130 for (auto s : graph.states()) dump(s);
131
132 for (auto succ : graph.successors()) {
133 for (auto dst : succ.second) {
134 for (auto t : graph.transitions(succ.first, dst)) {
135 out << to_label("State", succ.first) << " -> " << to_label("State", dst)
136 << " [ ";
137
138 dump(t);
139
140 out << " ]" << std::endl;
141 }
142 }
143 }
144
145 for (auto &kv : graph.to_pipe()) {
146 for (auto t : graph.to_pipe(kv.first)) {
147 out << to_label("State", kv.first) << " -> "
148 << to_label(::toString(gress) + "_pipe") << " [ ";
149
150 dump(t);
151
152 out << " ]" << std::endl;
153 }
154 }
155
156 for (auto &kv : graph.loopbacks()) {
157 auto next = graph.get_state(kv.first.second);
158
159 for (auto t : kv.second) {
160 out << to_label("State", kv.first.first) << " -> " << to_label("State", next)
161 << " [ color=\"red\" ";
162
163 dump(t);
164
165 out << " ]" << std::endl;
166 }
167 }
168 }
169
170 void dump(const IR::BFN::LoweredParserState *state) {
171 out << to_label("State", state) << " [style=filled, fillcolor=lightskyblue1, shape=record";
172 out << ", label=\"{";
173 out << state->name << ":\\l\\l";
174
175 if (detail) {
176 if (!state->select->regs.empty()) out << " " << state->select << "\\l";
177 }
178
179 out << "}\"";
180 out << "];" << std::endl;
181 }
182
183 void dump(const IR::BFN::LoweredParserMatch *match) {
184 out << to_label("Match", match) << " [style=filled, fillcolor=aliceblue, shape=record";
185 out << ", label=\"{";
186 out << "match " << match->value << ": \\l\\l";
187
188 if (detail) {
189 for (auto stmt : match->extracts) out << " " << stmt << "\\l";
190
191 if (match->extracts.size()) out << "\\l";
192
193 for (auto save : match->saves) out << " " << save << "\\l";
194
195 if (match->saves.size()) out << "\\l";
196
197 for (auto csum : match->checksums) out << " " << csum << "\\l";
198
199 if (match->checksums.size()) out << "\\l";
200
201 for (auto cntr : match->counters) out << " " << cntr << "\\l";
202
203 if (match->counters.size()) out << "\\l";
204
205 out << "shift: " << match->shift;
206 }
207
208 out << "}\"";
209 out << "];" << std::endl;
210 }
211
212 void dump(const IR::BFN::LoweredParserGraph &graph, gress_t gress) {
213 unsigned id = 0;
214 for (auto s : graph.states()) {
215 out << "subgraph cluster_" << id++ << " {";
216 out << "style=invis;" << std::endl;
217
218 dump(s);
219
220 for (auto m : s->transitions) dump(m);
221
222 out << "}" << std::endl;
223 }
224
225 for (auto s : graph.states()) {
226 for (auto m : s->transitions) {
227 out << to_label("State", s) << " -> " << to_label("Match", m) << std::endl;
228
229 if (m->next) {
230 out << to_label("Match", m) << " -> " << to_label("State", m->next)
231 << std::endl;
232 } else if (m->loop) {
233 // handled below
234 } else {
235 out << to_label("Match", m) << " -> " << to_label(::toString(gress) + "_pipe")
236 << std::endl;
237 }
238 }
239 }
240
241 // loopback edges
242 for (auto s : graph.states()) {
243 for (auto m : s->transitions) {
244 if (m->loop) {
245 auto next = graph.get_state(stripThreadPrefix(m->loop));
246 out << to_label("Match", m) << " -> " << to_label("State", next)
247 << " [color=\"red\"] " << std::endl;
248 }
249 }
250 }
251 }
252
253 template <typename ParserGraphType>
254 void dump_graph(const ParserGraphType &graph, gress_t gress, int pipe_id) {
255 out.str(std::string());
256
257 out << "digraph parser {" << std::endl;
258 out << "size=\"8,5\"" << std::endl;
259
260 dump(graph, gress);
261
262 out << "}" << std::endl;
263
264 if (auto fs = open_file(gress, pipe_id)) write_to_file(fs);
265 }
266
267 std::ofstream *open_file(gress_t gress, int pipe_id, cstring directory = "graphs"_cs) {
268 auto outdir = BFNContext::get().getOutputDirectory(directory, pipe_id);
269 if (!outdir) return nullptr;
270
271 static int fid = 0;
272 auto filepath = outdir + "/" + std::to_string(fid++) + "_" + filename + "_" +
273 ::toString(gress) + ".dot";
274
275 return new std::ofstream(filepath);
276 }
277
278 void write_to_file(std::ofstream *fs) {
279 *fs << escape(out.str());
280 fs->close();
281 }
282};
283
288class DumpParser : public Visitor, public DotDumper {
289 public:
290 explicit DumpParser(std::string filename, bool detail = false, bool to_log = false)
291 : DotDumper(filename, detail || LOGGING(4)), log(to_log) {}
292
293 DumpParser(std::string filename, std::vector<assoc::set<void *>> &color_groups,
294 bool detail = false, bool to_log = false)
295 : DotDumper(filename, color_groups, detail || LOGGING(4)), log(to_log) {}
296
297 private:
298 const IR::Node *apply_visitor(const IR::Node *n, const char *) override { return n; }
299
300 Visitor::profile_t init_apply(const IR::Node *root) override {
301 auto rv = Visitor::init_apply(root);
302
304 root->apply(cg);
305
307 root->apply(cgl);
308
309 if (!cg.graphs().empty() && !cgl.graphs().empty()) BUG("IR is in an incoherent state");
310
311 for (auto g : cg.graphs())
312 dump_graph(*(g.second), (g.first)->gress, root->to<IR::BFN::Pipe>()->canon_id());
313
314 for (auto g : cgl.graphs())
315 dump_graph(*(g.second), (g.first)->gress, root->to<IR::BFN::Pipe>()->canon_id());
316
317 if (log) {
318 // Use LOG level 1 as to let the caller decide on whether
319 // loogging should be done or not.
320 for (auto g : cg.graphs())
321 LOG1("Parser IR [" << filename << "] : " << std::endl
322 << dumpToString(g.first) << std::endl);
323
324 for (auto g : cgl.graphs())
325 LOG1("Lowered Parser IR [" << filename << "] : " << std::endl
326 << dumpToString(g.first) << std::endl);
327 }
328
329 return rv;
330 }
331
332 bool log;
333};
334
335#endif /* BACKENDS_TOFINO_BF_P4C_PARDE_DUMP_PARSER_H_ */
static BFNContext & get()
Definition bf-p4c-options.cpp:777
cstring getOutputDirectory(const cstring &suffix=cstring(), int pipe_id=-1)
Definition bf-p4c-options.cpp:795
Definition dump_parser.h:34
Definition node.h:95
Definition visitor.h:78
Definition visitor.h:75
Definition cstring.h:85
Definition assoc.h:355
Dumps the entire parser graphs (can be used before and also after parser lowering).
Definition dump_parser.h:288
Definition parser_info.h:186