1 //===- llvm/Support/GraphWriter.h - Write graph to a .dot file --*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a simple interface that can be used to print out generic
10 // LLVM graphs to ".dot" files. "dot" is a tool that is part of the AT&T
11 // graphviz package (http://www.research.att.com/sw/tools/graphviz/) which can
12 // be used to turn the files output by this interface into a variety of
13 // different graphics formats.
15 // Graphs do not need to implement any interface past what is already required
16 // by the GraphTraits template, but they can choose to implement specializations
17 // of the DOTGraphTraits template if they want to customize the graphs output in
20 //===----------------------------------------------------------------------===//
22 #ifndef LLVM_SUPPORT_GRAPHWRITER_H
23 #define LLVM_SUPPORT_GRAPHWRITER_H
25 #include "llvm/ADT/GraphTraits.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/ADT/Twine.h"
28 #include "llvm/Support/DOTGraphTraits.h"
29 #include "llvm/Support/FileSystem.h"
30 #include "llvm/Support/raw_ostream.h"
35 #include <type_traits>
40 namespace DOT
{ // Private functions...
42 std::string
EscapeString(const std::string
&Label
);
44 /// Get a color string for this node number. Simply round-robin selects
45 /// from a reasonable number of colors.
46 StringRef
getColorString(unsigned NodeNumber
);
48 } // end namespace DOT
50 namespace GraphProgram
{
60 } // end namespace GraphProgram
62 bool DisplayGraph(StringRef Filename
, bool wait
= true,
63 GraphProgram::Name program
= GraphProgram::DOT
);
65 template<typename GraphType
>
70 using DOTTraits
= DOTGraphTraits
<GraphType
>;
71 using GTraits
= GraphTraits
<GraphType
>;
72 using NodeRef
= typename
GTraits::NodeRef
;
73 using node_iterator
= typename
GTraits::nodes_iterator
;
74 using child_iterator
= typename
GTraits::ChildIteratorType
;
77 static_assert(std::is_pointer
<NodeRef
>::value
,
78 "FIXME: Currently GraphWriter requires the NodeRef type to be "
79 "a pointer.\nThe pointer usage should be moved to "
80 "DOTGraphTraits, and removed from GraphWriter itself.");
82 // Writes the edge labels of the node to O and returns true if there are any
83 // edge labels not equal to the empty string "".
84 bool getEdgeSourceLabels(raw_ostream
&O
, NodeRef Node
) {
85 child_iterator EI
= GTraits::child_begin(Node
);
86 child_iterator EE
= GTraits::child_end(Node
);
87 bool hasEdgeSourceLabels
= false;
89 for (unsigned i
= 0; EI
!= EE
&& i
!= 64; ++EI
, ++i
) {
90 std::string label
= DTraits
.getEdgeSourceLabel(Node
, EI
);
95 hasEdgeSourceLabels
= true;
100 O
<< "<s" << i
<< ">" << DOT::EscapeString(label
);
103 if (EI
!= EE
&& hasEdgeSourceLabels
)
104 O
<< "|<s64>truncated...";
106 return hasEdgeSourceLabels
;
110 GraphWriter(raw_ostream
&o
, const GraphType
&g
, bool SN
) : O(o
), G(g
) {
111 DTraits
= DOTTraits(SN
);
114 void writeGraph(const std::string
&Title
= "") {
115 // Output the header for the graph...
118 // Emit all of the nodes in the graph...
121 // Output any customizations on the graph
122 DOTGraphTraits
<GraphType
>::addCustomGraphFeatures(G
, *this);
124 // Output the end of the graph
128 void writeHeader(const std::string
&Title
) {
129 std::string GraphName
= DTraits
.getGraphName(G
);
132 O
<< "digraph \"" << DOT::EscapeString(Title
) << "\" {\n";
133 else if (!GraphName
.empty())
134 O
<< "digraph \"" << DOT::EscapeString(GraphName
) << "\" {\n";
136 O
<< "digraph unnamed {\n";
138 if (DTraits
.renderGraphFromBottomUp())
139 O
<< "\trankdir=\"BT\";\n";
142 O
<< "\tlabel=\"" << DOT::EscapeString(Title
) << "\";\n";
143 else if (!GraphName
.empty())
144 O
<< "\tlabel=\"" << DOT::EscapeString(GraphName
) << "\";\n";
145 O
<< DTraits
.getGraphProperties(G
);
150 // Finish off the graph
155 // Loop over the graph, printing it out...
156 for (const auto Node
: nodes
<GraphType
>(G
))
157 if (!isNodeHidden(Node
))
161 bool isNodeHidden(NodeRef Node
) {
162 return DTraits
.isNodeHidden(Node
);
165 void writeNode(NodeRef Node
) {
166 std::string NodeAttributes
= DTraits
.getNodeAttributes(Node
, G
);
168 O
<< "\tNode" << static_cast<const void*>(Node
) << " [shape=record,";
169 if (!NodeAttributes
.empty()) O
<< NodeAttributes
<< ",";
172 if (!DTraits
.renderGraphFromBottomUp()) {
173 O
<< DOT::EscapeString(DTraits
.getNodeLabel(Node
, G
));
175 // If we should include the address of the node in the label, do so now.
176 std::string Id
= DTraits
.getNodeIdentifierLabel(Node
, G
);
178 O
<< "|" << DOT::EscapeString(Id
);
180 std::string NodeDesc
= DTraits
.getNodeDescription(Node
, G
);
181 if (!NodeDesc
.empty())
182 O
<< "|" << DOT::EscapeString(NodeDesc
);
185 std::string edgeSourceLabels
;
186 raw_string_ostream
EdgeSourceLabels(edgeSourceLabels
);
187 bool hasEdgeSourceLabels
= getEdgeSourceLabels(EdgeSourceLabels
, Node
);
189 if (hasEdgeSourceLabels
) {
190 if (!DTraits
.renderGraphFromBottomUp()) O
<< "|";
192 O
<< "{" << EdgeSourceLabels
.str() << "}";
194 if (DTraits
.renderGraphFromBottomUp()) O
<< "|";
197 if (DTraits
.renderGraphFromBottomUp()) {
198 O
<< DOT::EscapeString(DTraits
.getNodeLabel(Node
, G
));
200 // If we should include the address of the node in the label, do so now.
201 std::string Id
= DTraits
.getNodeIdentifierLabel(Node
, G
);
203 O
<< "|" << DOT::EscapeString(Id
);
205 std::string NodeDesc
= DTraits
.getNodeDescription(Node
, G
);
206 if (!NodeDesc
.empty())
207 O
<< "|" << DOT::EscapeString(NodeDesc
);
210 if (DTraits
.hasEdgeDestLabels()) {
213 unsigned i
= 0, e
= DTraits
.numEdgeDestLabels(Node
);
214 for (; i
!= e
&& i
!= 64; ++i
) {
216 O
<< "<d" << i
<< ">"
217 << DOT::EscapeString(DTraits
.getEdgeDestLabel(Node
, i
));
221 O
<< "|<d64>truncated...";
225 O
<< "}\"];\n"; // Finish printing the "node" line
227 // Output all of the edges now
228 child_iterator EI
= GTraits::child_begin(Node
);
229 child_iterator EE
= GTraits::child_end(Node
);
230 for (unsigned i
= 0; EI
!= EE
&& i
!= 64; ++EI
, ++i
)
231 if (!DTraits
.isNodeHidden(*EI
))
232 writeEdge(Node
, i
, EI
);
233 for (; EI
!= EE
; ++EI
)
234 if (!DTraits
.isNodeHidden(*EI
))
235 writeEdge(Node
, 64, EI
);
238 void writeEdge(NodeRef Node
, unsigned edgeidx
, child_iterator EI
) {
239 if (NodeRef TargetNode
= *EI
) {
241 if (DTraits
.edgeTargetsEdgeSource(Node
, EI
)) {
242 child_iterator TargetIt
= DTraits
.getEdgeTarget(Node
, EI
);
244 // Figure out which edge this targets...
246 (unsigned)std::distance(GTraits::child_begin(TargetNode
), TargetIt
);
247 DestPort
= static_cast<int>(Offset
);
250 if (DTraits
.getEdgeSourceLabel(Node
, EI
).empty())
253 emitEdge(static_cast<const void*>(Node
), edgeidx
,
254 static_cast<const void*>(TargetNode
), DestPort
,
255 DTraits
.getEdgeAttributes(Node
, EI
, G
));
259 /// emitSimpleNode - Outputs a simple (non-record) node
260 void emitSimpleNode(const void *ID
, const std::string
&Attr
,
261 const std::string
&Label
, unsigned NumEdgeSources
= 0,
262 const std::vector
<std::string
> *EdgeSourceLabels
= nullptr) {
263 O
<< "\tNode" << ID
<< "[ ";
267 if (NumEdgeSources
) O
<< "{";
268 O
<< DOT::EscapeString(Label
);
269 if (NumEdgeSources
) {
272 for (unsigned i
= 0; i
!= NumEdgeSources
; ++i
) {
274 O
<< "<s" << i
<< ">";
275 if (EdgeSourceLabels
) O
<< DOT::EscapeString((*EdgeSourceLabels
)[i
]);
282 /// emitEdge - Output an edge from a simple node into the graph...
283 void emitEdge(const void *SrcNodeID
, int SrcNodePort
,
284 const void *DestNodeID
, int DestNodePort
,
285 const std::string
&Attrs
) {
286 if (SrcNodePort
> 64) return; // Eminating from truncated part?
287 if (DestNodePort
> 64) DestNodePort
= 64; // Targeting the truncated part?
289 O
<< "\tNode" << SrcNodeID
;
290 if (SrcNodePort
>= 0)
291 O
<< ":s" << SrcNodePort
;
292 O
<< " -> Node" << DestNodeID
;
293 if (DestNodePort
>= 0 && DTraits
.hasEdgeDestLabels())
294 O
<< ":d" << DestNodePort
;
297 O
<< "[" << Attrs
<< "]";
301 /// getOStream - Get the raw output stream into the graph file. Useful to
302 /// write fancy things using addCustomGraphFeatures().
303 raw_ostream
&getOStream() {
308 template<typename GraphType
>
309 raw_ostream
&WriteGraph(raw_ostream
&O
, const GraphType
&G
,
310 bool ShortNames
= false,
311 const Twine
&Title
= "") {
312 // Start the graph emission process...
313 GraphWriter
<GraphType
> W(O
, G
, ShortNames
);
316 W
.writeGraph(Title
.str());
321 std::string
createGraphFilename(const Twine
&Name
, int &FD
);
323 /// Writes graph into a provided {@code Filename}.
324 /// If {@code Filename} is empty, generates a random one.
325 /// \return The resulting filename, or an empty string if writing
327 template <typename GraphType
>
328 std::string
WriteGraph(const GraphType
&G
, const Twine
&Name
,
329 bool ShortNames
= false,
330 const Twine
&Title
= "",
331 std::string Filename
= "") {
333 // Windows can't always handle long paths, so limit the length of the name.
334 std::string N
= Name
.str();
335 N
= N
.substr(0, std::min
<std::size_t>(N
.size(), 140));
336 if (Filename
.empty()) {
337 Filename
= createGraphFilename(N
, FD
);
339 std::error_code EC
= sys::fs::openFileForWrite(Filename
, FD
);
341 // Writing over an existing file is not considered an error.
342 if (EC
== std::errc::file_exists
) {
343 errs() << "file exists, overwriting" << "\n";
345 errs() << "error writing into file" << "\n";
349 raw_fd_ostream
O(FD
, /*shouldClose=*/ true);
352 errs() << "error opening file '" << Filename
<< "' for writing!\n";
356 llvm::WriteGraph(O
, G
, ShortNames
, Title
);
357 errs() << " done. \n";
362 /// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file,
363 /// then cleanup. For use from the debugger.
365 template<typename GraphType
>
366 void ViewGraph(const GraphType
&G
, const Twine
&Name
,
367 bool ShortNames
= false, const Twine
&Title
= "",
368 GraphProgram::Name Program
= GraphProgram::DOT
) {
369 std::string Filename
= llvm::WriteGraph(G
, Name
, ShortNames
, Title
);
371 if (Filename
.empty())
374 DisplayGraph(Filename
, false, Program
);
377 } // end namespace llvm
379 #endif // LLVM_SUPPORT_GRAPHWRITER_H