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