blob: 02d98bec16e2b3bb6ab83978782d85b0110a1894 [file] [log] [blame]
Andrew Scull5e1ddfa2018-08-14 10:06:54 +01001//===- llvm/Support/GraphWriter.h - Write graph to a .dot file --*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines a simple interface that can be used to print out generic
11// LLVM graphs to ".dot" files. "dot" is a tool that is part of the AT&T
12// graphviz package (http://www.research.att.com/sw/tools/graphviz/) which can
13// be used to turn the files output by this interface into a variety of
14// different graphics formats.
15//
16// Graphs do not need to implement any interface past what is already required
17// by the GraphTraits template, but they can choose to implement specializations
18// of the DOTGraphTraits template if they want to customize the graphs output in
19// any way.
20//
21//===----------------------------------------------------------------------===//
22
23#ifndef LLVM_SUPPORT_GRAPHWRITER_H
24#define LLVM_SUPPORT_GRAPHWRITER_H
25
26#include "llvm/ADT/GraphTraits.h"
27#include "llvm/ADT/StringRef.h"
28#include "llvm/ADT/Twine.h"
29#include "llvm/Support/DOTGraphTraits.h"
Andrew Scull0372a572018-11-16 15:47:06 +000030#include "llvm/Support/FileSystem.h"
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010031#include "llvm/Support/raw_ostream.h"
32#include <algorithm>
33#include <cstddef>
34#include <iterator>
35#include <string>
36#include <type_traits>
37#include <vector>
38
39namespace llvm {
40
41namespace DOT { // Private functions...
42
43std::string EscapeString(const std::string &Label);
44
Andrew Scullcdfcccc2018-10-05 20:58:37 +010045/// Get a color string for this node number. Simply round-robin selects
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010046/// from a reasonable number of colors.
47StringRef getColorString(unsigned NodeNumber);
48
49} // end namespace DOT
50
51namespace GraphProgram {
52
53enum Name {
54 DOT,
55 FDP,
56 NEATO,
57 TWOPI,
58 CIRCO
59};
60
61} // end namespace GraphProgram
62
63bool DisplayGraph(StringRef Filename, bool wait = true,
64 GraphProgram::Name program = GraphProgram::DOT);
65
66template<typename GraphType>
67class GraphWriter {
68 raw_ostream &O;
69 const GraphType &G;
70
71 using DOTTraits = DOTGraphTraits<GraphType>;
72 using GTraits = GraphTraits<GraphType>;
73 using NodeRef = typename GTraits::NodeRef;
74 using node_iterator = typename GTraits::nodes_iterator;
75 using child_iterator = typename GTraits::ChildIteratorType;
76 DOTTraits DTraits;
77
78 static_assert(std::is_pointer<NodeRef>::value,
79 "FIXME: Currently GraphWriter requires the NodeRef type to be "
80 "a pointer.\nThe pointer usage should be moved to "
81 "DOTGraphTraits, and removed from GraphWriter itself.");
82
83 // Writes the edge labels of the node to O and returns true if there are any
84 // edge labels not equal to the empty string "".
85 bool getEdgeSourceLabels(raw_ostream &O, NodeRef Node) {
86 child_iterator EI = GTraits::child_begin(Node);
87 child_iterator EE = GTraits::child_end(Node);
88 bool hasEdgeSourceLabels = false;
89
90 for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) {
91 std::string label = DTraits.getEdgeSourceLabel(Node, EI);
92
93 if (label.empty())
94 continue;
95
96 hasEdgeSourceLabels = true;
97
98 if (i)
99 O << "|";
100
101 O << "<s" << i << ">" << DOT::EscapeString(label);
102 }
103
104 if (EI != EE && hasEdgeSourceLabels)
105 O << "|<s64>truncated...";
106
107 return hasEdgeSourceLabels;
108 }
109
110public:
111 GraphWriter(raw_ostream &o, const GraphType &g, bool SN) : O(o), G(g) {
112 DTraits = DOTTraits(SN);
113 }
114
115 void writeGraph(const std::string &Title = "") {
116 // Output the header for the graph...
117 writeHeader(Title);
118
119 // Emit all of the nodes in the graph...
120 writeNodes();
121
122 // Output any customizations on the graph
123 DOTGraphTraits<GraphType>::addCustomGraphFeatures(G, *this);
124
125 // Output the end of the graph
126 writeFooter();
127 }
128
129 void writeHeader(const std::string &Title) {
130 std::string GraphName = DTraits.getGraphName(G);
131
132 if (!Title.empty())
133 O << "digraph \"" << DOT::EscapeString(Title) << "\" {\n";
134 else if (!GraphName.empty())
135 O << "digraph \"" << DOT::EscapeString(GraphName) << "\" {\n";
136 else
137 O << "digraph unnamed {\n";
138
139 if (DTraits.renderGraphFromBottomUp())
140 O << "\trankdir=\"BT\";\n";
141
142 if (!Title.empty())
143 O << "\tlabel=\"" << DOT::EscapeString(Title) << "\";\n";
144 else if (!GraphName.empty())
145 O << "\tlabel=\"" << DOT::EscapeString(GraphName) << "\";\n";
146 O << DTraits.getGraphProperties(G);
147 O << "\n";
148 }
149
150 void writeFooter() {
151 // Finish off the graph
152 O << "}\n";
153 }
154
155 void writeNodes() {
156 // Loop over the graph, printing it out...
157 for (const auto Node : nodes<GraphType>(G))
158 if (!isNodeHidden(Node))
159 writeNode(Node);
160 }
161
162 bool isNodeHidden(NodeRef Node) {
163 return DTraits.isNodeHidden(Node);
164 }
165
166 void writeNode(NodeRef Node) {
167 std::string NodeAttributes = DTraits.getNodeAttributes(Node, G);
168
169 O << "\tNode" << static_cast<const void*>(Node) << " [shape=record,";
170 if (!NodeAttributes.empty()) O << NodeAttributes << ",";
171 O << "label=\"{";
172
173 if (!DTraits.renderGraphFromBottomUp()) {
174 O << DOT::EscapeString(DTraits.getNodeLabel(Node, G));
175
176 // If we should include the address of the node in the label, do so now.
177 std::string Id = DTraits.getNodeIdentifierLabel(Node, G);
178 if (!Id.empty())
179 O << "|" << DOT::EscapeString(Id);
180
181 std::string NodeDesc = DTraits.getNodeDescription(Node, G);
182 if (!NodeDesc.empty())
183 O << "|" << DOT::EscapeString(NodeDesc);
184 }
185
186 std::string edgeSourceLabels;
187 raw_string_ostream EdgeSourceLabels(edgeSourceLabels);
188 bool hasEdgeSourceLabels = getEdgeSourceLabels(EdgeSourceLabels, Node);
189
190 if (hasEdgeSourceLabels) {
191 if (!DTraits.renderGraphFromBottomUp()) O << "|";
192
193 O << "{" << EdgeSourceLabels.str() << "}";
194
195 if (DTraits.renderGraphFromBottomUp()) O << "|";
196 }
197
198 if (DTraits.renderGraphFromBottomUp()) {
199 O << DOT::EscapeString(DTraits.getNodeLabel(Node, G));
200
201 // If we should include the address of the node in the label, do so now.
202 std::string Id = DTraits.getNodeIdentifierLabel(Node, G);
203 if (!Id.empty())
204 O << "|" << DOT::EscapeString(Id);
205
206 std::string NodeDesc = DTraits.getNodeDescription(Node, G);
207 if (!NodeDesc.empty())
208 O << "|" << DOT::EscapeString(NodeDesc);
209 }
210
211 if (DTraits.hasEdgeDestLabels()) {
212 O << "|{";
213
214 unsigned i = 0, e = DTraits.numEdgeDestLabels(Node);
215 for (; i != e && i != 64; ++i) {
216 if (i) O << "|";
217 O << "<d" << i << ">"
218 << DOT::EscapeString(DTraits.getEdgeDestLabel(Node, i));
219 }
220
221 if (i != e)
222 O << "|<d64>truncated...";
223 O << "}";
224 }
225
226 O << "}\"];\n"; // Finish printing the "node" line
227
228 // Output all of the edges now
229 child_iterator EI = GTraits::child_begin(Node);
230 child_iterator EE = GTraits::child_end(Node);
231 for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i)
232 if (!DTraits.isNodeHidden(*EI))
233 writeEdge(Node, i, EI);
234 for (; EI != EE; ++EI)
235 if (!DTraits.isNodeHidden(*EI))
236 writeEdge(Node, 64, EI);
237 }
238
239 void writeEdge(NodeRef Node, unsigned edgeidx, child_iterator EI) {
240 if (NodeRef TargetNode = *EI) {
241 int DestPort = -1;
242 if (DTraits.edgeTargetsEdgeSource(Node, EI)) {
243 child_iterator TargetIt = DTraits.getEdgeTarget(Node, EI);
244
245 // Figure out which edge this targets...
246 unsigned Offset =
247 (unsigned)std::distance(GTraits::child_begin(TargetNode), TargetIt);
248 DestPort = static_cast<int>(Offset);
249 }
250
251 if (DTraits.getEdgeSourceLabel(Node, EI).empty())
252 edgeidx = -1;
253
254 emitEdge(static_cast<const void*>(Node), edgeidx,
255 static_cast<const void*>(TargetNode), DestPort,
256 DTraits.getEdgeAttributes(Node, EI, G));
257 }
258 }
259
260 /// emitSimpleNode - Outputs a simple (non-record) node
261 void emitSimpleNode(const void *ID, const std::string &Attr,
262 const std::string &Label, unsigned NumEdgeSources = 0,
263 const std::vector<std::string> *EdgeSourceLabels = nullptr) {
264 O << "\tNode" << ID << "[ ";
265 if (!Attr.empty())
266 O << Attr << ",";
267 O << " label =\"";
268 if (NumEdgeSources) O << "{";
269 O << DOT::EscapeString(Label);
270 if (NumEdgeSources) {
271 O << "|{";
272
273 for (unsigned i = 0; i != NumEdgeSources; ++i) {
274 if (i) O << "|";
275 O << "<s" << i << ">";
276 if (EdgeSourceLabels) O << DOT::EscapeString((*EdgeSourceLabels)[i]);
277 }
278 O << "}}";
279 }
280 O << "\"];\n";
281 }
282
283 /// emitEdge - Output an edge from a simple node into the graph...
284 void emitEdge(const void *SrcNodeID, int SrcNodePort,
285 const void *DestNodeID, int DestNodePort,
286 const std::string &Attrs) {
287 if (SrcNodePort > 64) return; // Eminating from truncated part?
288 if (DestNodePort > 64) DestNodePort = 64; // Targeting the truncated part?
289
290 O << "\tNode" << SrcNodeID;
291 if (SrcNodePort >= 0)
292 O << ":s" << SrcNodePort;
293 O << " -> Node" << DestNodeID;
294 if (DestNodePort >= 0 && DTraits.hasEdgeDestLabels())
295 O << ":d" << DestNodePort;
296
297 if (!Attrs.empty())
298 O << "[" << Attrs << "]";
299 O << ";\n";
300 }
301
302 /// getOStream - Get the raw output stream into the graph file. Useful to
303 /// write fancy things using addCustomGraphFeatures().
304 raw_ostream &getOStream() {
305 return O;
306 }
307};
308
309template<typename GraphType>
310raw_ostream &WriteGraph(raw_ostream &O, const GraphType &G,
311 bool ShortNames = false,
312 const Twine &Title = "") {
313 // Start the graph emission process...
314 GraphWriter<GraphType> W(O, G, ShortNames);
315
316 // Emit the graph.
317 W.writeGraph(Title.str());
318
319 return O;
320}
321
322std::string createGraphFilename(const Twine &Name, int &FD);
323
Andrew Scull0372a572018-11-16 15:47:06 +0000324/// Writes graph into a provided {@code Filename}.
325/// If {@code Filename} is empty, generates a random one.
326/// \return The resulting filename, or an empty string if writing
327/// failed.
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100328template <typename GraphType>
329std::string WriteGraph(const GraphType &G, const Twine &Name,
Andrew Scull0372a572018-11-16 15:47:06 +0000330 bool ShortNames = false,
331 const Twine &Title = "",
332 std::string Filename = "") {
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100333 int FD;
334 // Windows can't always handle long paths, so limit the length of the name.
335 std::string N = Name.str();
336 N = N.substr(0, std::min<std::size_t>(N.size(), 140));
Andrew Scull0372a572018-11-16 15:47:06 +0000337 if (Filename.empty()) {
338 Filename = createGraphFilename(N, FD);
339 } else {
340 std::error_code EC = sys::fs::openFileForWrite(Filename, FD);
341
342 // Writing over an existing file is not considered an error.
343 if (EC == std::errc::file_exists) {
344 errs() << "file exists, overwriting" << "\n";
345 } else if (EC) {
346 errs() << "error writing into file" << "\n";
347 return "";
348 }
349 }
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100350 raw_fd_ostream O(FD, /*shouldClose=*/ true);
351
352 if (FD == -1) {
353 errs() << "error opening file '" << Filename << "' for writing!\n";
354 return "";
355 }
356
357 llvm::WriteGraph(O, G, ShortNames, Title);
358 errs() << " done. \n";
359
360 return Filename;
361}
362
363/// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file,
364/// then cleanup. For use from the debugger.
365///
366template<typename GraphType>
367void ViewGraph(const GraphType &G, const Twine &Name,
368 bool ShortNames = false, const Twine &Title = "",
369 GraphProgram::Name Program = GraphProgram::DOT) {
370 std::string Filename = llvm::WriteGraph(G, Name, ShortNames, Title);
371
372 if (Filename.empty())
373 return;
374
375 DisplayGraph(Filename, false, Program);
376}
377
378} // end namespace llvm
379
380#endif // LLVM_SUPPORT_GRAPHWRITER_H