GraphWriter.h revision 208954
1//===-- 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/Support/DOTGraphTraits.h"
27#include "llvm/Support/raw_ostream.h"
28#include "llvm/ADT/GraphTraits.h"
29#include "llvm/System/Path.h"
30#include <vector>
31#include <cassert>
32
33namespace llvm {
34
35namespace DOT {  // Private functions...
36  std::string EscapeString(const std::string &Label);
37}
38
39namespace GraphProgram {
40   enum Name {
41      DOT,
42      FDP,
43      NEATO,
44      TWOPI,
45      CIRCO
46   };
47}
48
49void DisplayGraph(const sys::Path& Filename, bool wait=true, GraphProgram::Name program = GraphProgram::DOT);
50
51template<typename GraphType>
52class GraphWriter {
53  raw_ostream &O;
54  const GraphType &G;
55
56  typedef DOTGraphTraits<GraphType>           DOTTraits;
57  typedef GraphTraits<GraphType>              GTraits;
58  typedef typename GTraits::NodeType          NodeType;
59  typedef typename GTraits::nodes_iterator    node_iterator;
60  typedef typename GTraits::ChildIteratorType child_iterator;
61  DOTTraits DTraits;
62
63  // Writes the edge labels of the node to O and returns true if there are any
64  // edge labels not equal to the empty string "".
65  bool getEdgeSourceLabels(raw_ostream &O, NodeType *Node) {
66    child_iterator EI = GTraits::child_begin(Node);
67    child_iterator EE = GTraits::child_end(Node);
68    bool hasEdgeSourceLabels = false;
69
70    for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) {
71      std::string label = DTraits.getEdgeSourceLabel(Node, EI);
72
73      if (label == "")
74        continue;
75
76      hasEdgeSourceLabels = true;
77
78      if (i)
79        O << "|";
80
81      O << "<s" << i << ">" << DTraits.getEdgeSourceLabel(Node, EI);
82    }
83
84    if (EI != EE && hasEdgeSourceLabels)
85      O << "|<s64>truncated...";
86
87    return hasEdgeSourceLabels;
88  }
89
90public:
91  GraphWriter(raw_ostream &o, const GraphType &g, bool SN) : O(o), G(g) {
92  DTraits = DOTTraits(SN);
93}
94
95  void writeHeader(const std::string &Name) {
96    std::string GraphName = DTraits.getGraphName(G);
97
98    if (!Name.empty())
99      O << "digraph \"" << DOT::EscapeString(Name) << "\" {\n";
100    else if (!GraphName.empty())
101      O << "digraph \"" << DOT::EscapeString(GraphName) << "\" {\n";
102    else
103      O << "digraph unnamed {\n";
104
105    if (DTraits.renderGraphFromBottomUp())
106      O << "\trankdir=\"BT\";\n";
107
108    if (!Name.empty())
109      O << "\tlabel=\"" << DOT::EscapeString(Name) << "\";\n";
110    else if (!GraphName.empty())
111      O << "\tlabel=\"" << DOT::EscapeString(GraphName) << "\";\n";
112    O << DTraits.getGraphProperties(G);
113    O << "\n";
114  }
115
116  void writeFooter() {
117    // Finish off the graph
118    O << "}\n";
119  }
120
121  void writeNodes() {
122    // Loop over the graph, printing it out...
123    for (node_iterator I = GTraits::nodes_begin(G), E = GTraits::nodes_end(G);
124         I != E; ++I)
125      if (!isNodeHidden(*I))
126        writeNode(*I);
127  }
128
129  bool isNodeHidden(NodeType &Node) {
130    return isNodeHidden(&Node);
131  }
132
133  bool isNodeHidden(NodeType *const *Node) {
134    return isNodeHidden(*Node);
135  }
136
137  bool isNodeHidden(NodeType *Node) {
138    return DTraits.isNodeHidden(Node);
139  }
140
141  void writeNode(NodeType& Node) {
142    writeNode(&Node);
143  }
144
145  void writeNode(NodeType *const *Node) {
146    writeNode(*Node);
147  }
148
149  void writeNode(NodeType *Node) {
150    std::string NodeAttributes = DTraits.getNodeAttributes(Node, G);
151
152    O << "\tNode" << static_cast<const void*>(Node) << " [shape=record,";
153    if (!NodeAttributes.empty()) O << NodeAttributes << ",";
154    O << "label=\"{";
155
156    if (!DTraits.renderGraphFromBottomUp()) {
157      O << DOT::EscapeString(DTraits.getNodeLabel(Node, G));
158
159      // If we should include the address of the node in the label, do so now.
160      if (DTraits.hasNodeAddressLabel(Node, G))
161        O << "|" << (void*)Node;
162    }
163
164    std::string edgeSourceLabels;
165    raw_string_ostream EdgeSourceLabels(edgeSourceLabels);
166    bool hasEdgeSourceLabels = getEdgeSourceLabels(EdgeSourceLabels, Node);
167
168    if (hasEdgeSourceLabels) {
169      if (!DTraits.renderGraphFromBottomUp()) O << "|";
170
171      O << "{" << EdgeSourceLabels.str() << "}";
172
173      if (DTraits.renderGraphFromBottomUp()) O << "|";
174    }
175
176    if (DTraits.renderGraphFromBottomUp()) {
177      O << DOT::EscapeString(DTraits.getNodeLabel(Node, G));
178
179      // If we should include the address of the node in the label, do so now.
180      if (DTraits.hasNodeAddressLabel(Node, G))
181        O << "|" << (void*)Node;
182    }
183
184    if (DTraits.hasEdgeDestLabels()) {
185      O << "|{";
186
187      unsigned i = 0, e = DTraits.numEdgeDestLabels(Node);
188      for (; i != e && i != 64; ++i) {
189        if (i) O << "|";
190        O << "<d" << i << ">"
191          << DOT::EscapeString(DTraits.getEdgeDestLabel(Node, i));
192      }
193
194      if (i != e)
195        O << "|<d64>truncated...";
196      O << "}";
197    }
198
199    O << "}\"];\n";   // Finish printing the "node" line
200
201    // Output all of the edges now
202    child_iterator EI = GTraits::child_begin(Node);
203    child_iterator EE = GTraits::child_end(Node);
204    for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i)
205      if (!DTraits.isNodeHidden(*EI))
206        writeEdge(Node, i, EI);
207    for (; EI != EE; ++EI)
208      if (!DTraits.isNodeHidden(*EI))
209        writeEdge(Node, 64, EI);
210  }
211
212  void writeEdge(NodeType *Node, unsigned edgeidx, child_iterator EI) {
213    if (NodeType *TargetNode = *EI) {
214      int DestPort = -1;
215      if (DTraits.edgeTargetsEdgeSource(Node, EI)) {
216        child_iterator TargetIt = DTraits.getEdgeTarget(Node, EI);
217
218        // Figure out which edge this targets...
219        unsigned Offset =
220          (unsigned)std::distance(GTraits::child_begin(TargetNode), TargetIt);
221        DestPort = static_cast<int>(Offset);
222      }
223
224      if (DTraits.getEdgeSourceLabel(Node, EI) == "")
225        edgeidx = -1;
226
227      emitEdge(static_cast<const void*>(Node), edgeidx,
228               static_cast<const void*>(TargetNode), DestPort,
229               DTraits.getEdgeAttributes(Node, EI));
230    }
231  }
232
233  /// emitSimpleNode - Outputs a simple (non-record) node
234  void emitSimpleNode(const void *ID, const std::string &Attr,
235                      const std::string &Label, unsigned NumEdgeSources = 0,
236                      const std::vector<std::string> *EdgeSourceLabels = 0) {
237    O << "\tNode" << ID << "[ ";
238    if (!Attr.empty())
239      O << Attr << ",";
240    O << " label =\"";
241    if (NumEdgeSources) O << "{";
242    O << DOT::EscapeString(Label);
243    if (NumEdgeSources) {
244      O << "|{";
245
246      for (unsigned i = 0; i != NumEdgeSources; ++i) {
247        if (i) O << "|";
248        O << "<s" << i << ">";
249        if (EdgeSourceLabels) O << DOT::EscapeString((*EdgeSourceLabels)[i]);
250      }
251      O << "}}";
252    }
253    O << "\"];\n";
254  }
255
256  /// emitEdge - Output an edge from a simple node into the graph...
257  void emitEdge(const void *SrcNodeID, int SrcNodePort,
258                const void *DestNodeID, int DestNodePort,
259                const std::string &Attrs) {
260    if (SrcNodePort  > 64) return;             // Eminating from truncated part?
261    if (DestNodePort > 64) DestNodePort = 64;  // Targetting the truncated part?
262
263    O << "\tNode" << SrcNodeID;
264    if (SrcNodePort >= 0)
265      O << ":s" << SrcNodePort;
266    O << " -> Node" << DestNodeID;
267    if (DestNodePort >= 0 && DTraits.hasEdgeDestLabels())
268      O << ":d" << DestNodePort;
269
270    if (!Attrs.empty())
271      O << "[" << Attrs << "]";
272    O << ";\n";
273  }
274};
275
276template<typename GraphType>
277raw_ostream &WriteGraph(raw_ostream &O, const GraphType &G,
278                        bool ShortNames = false,
279                        const std::string &Name = "",
280                        const std::string &Title = "") {
281  // Start the graph emission process...
282  GraphWriter<GraphType> W(O, G, ShortNames);
283
284  // Output the header for the graph...
285  W.writeHeader(Title);
286
287  // Emit all of the nodes in the graph...
288  W.writeNodes();
289
290  // Output any customizations on the graph
291  DOTGraphTraits<GraphType>::addCustomGraphFeatures(G, W);
292
293  // Output the end of the graph
294  W.writeFooter();
295  return O;
296}
297
298template<typename GraphType>
299sys::Path WriteGraph(const GraphType &G, const std::string &Name,
300                     bool ShortNames = false, const std::string &Title = "") {
301  std::string ErrMsg;
302  sys::Path Filename = sys::Path::GetTemporaryDirectory(&ErrMsg);
303  if (Filename.isEmpty()) {
304    errs() << "Error: " << ErrMsg << "\n";
305    return Filename;
306  }
307  Filename.appendComponent(Name + ".dot");
308  if (Filename.makeUnique(true,&ErrMsg)) {
309    errs() << "Error: " << ErrMsg << "\n";
310    return sys::Path();
311  }
312
313  errs() << "Writing '" << Filename.str() << "'... ";
314
315  std::string ErrorInfo;
316  raw_fd_ostream O(Filename.c_str(), ErrorInfo);
317
318  if (ErrorInfo.empty()) {
319    WriteGraph(O, G, ShortNames, Name, Title);
320    errs() << " done. \n";
321  } else {
322    errs() << "error opening file '" << Filename.str() << "' for writing!\n";
323    Filename.clear();
324  }
325
326  return Filename;
327}
328
329/// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file,
330/// then cleanup.  For use from the debugger.
331///
332template<typename GraphType>
333void ViewGraph(const GraphType &G, const std::string &Name,
334               bool ShortNames = false, const std::string &Title = "",
335               GraphProgram::Name Program = GraphProgram::DOT) {
336  sys::Path Filename = WriteGraph(G, Name, ShortNames, Title);
337
338  if (Filename.isEmpty())
339    return;
340
341  DisplayGraph(Filename, true, Program);
342}
343
344} // End llvm namespace
345
346#endif
347