LLVM API Documentation
00001 //===-- llvm/Support/GraphWriter.h - Write graph to a .dot file -*- C++ -*-===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file was developed by the LLVM research group and is distributed under 00006 // the University of Illinois Open Source License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines a simple interface that can be used to print out generic 00011 // LLVM graphs to ".dot" files. "dot" is a tool that is part of the AT&T 00012 // graphviz package (http://www.research.att.com/sw/tools/graphviz/) which can 00013 // be used to turn the files output by this interface into a variety of 00014 // different graphics formats. 00015 // 00016 // Graphs do not need to implement any interface past what is already required 00017 // by the GraphTraits template, but they can choose to implement specializations 00018 // of the DOTGraphTraits template if they want to customize the graphs output in 00019 // any way. 00020 // 00021 //===----------------------------------------------------------------------===// 00022 00023 #ifndef LLVM_SUPPORT_GRAPHWRITER_H 00024 #define LLVM_SUPPORT_GRAPHWRITER_H 00025 00026 #include "llvm/Support/DOTGraphTraits.h" 00027 #include "llvm/ADT/GraphTraits.h" 00028 #include "llvm/System/Path.h" 00029 #include <vector> 00030 #include <iostream> 00031 #include <fstream> 00032 00033 namespace llvm { 00034 00035 namespace DOT { // Private functions... 00036 inline std::string EscapeString(const std::string &Label) { 00037 std::string Str(Label); 00038 for (unsigned i = 0; i != Str.length(); ++i) 00039 switch (Str[i]) { 00040 case '\n': 00041 Str.insert(Str.begin()+i, '\\'); // Escape character... 00042 ++i; 00043 Str[i] = 'n'; 00044 break; 00045 case '\t': 00046 Str.insert(Str.begin()+i, ' '); // Convert to two spaces 00047 ++i; 00048 Str[i] = ' '; 00049 break; 00050 case '\\': 00051 if (i+1 != Str.length() && Str[i+1] == 'l') 00052 break; // don't disturb \l 00053 case '{': case '}': 00054 case '<': case '>': 00055 case '"': 00056 Str.insert(Str.begin()+i, '\\'); // Escape character... 00057 ++i; // don't infinite loop 00058 break; 00059 } 00060 return Str; 00061 } 00062 } 00063 00064 void DisplayGraph(const sys::Path& Filename); 00065 00066 template<typename GraphType> 00067 class GraphWriter { 00068 std::ostream &O; 00069 const GraphType &G; 00070 00071 typedef DOTGraphTraits<GraphType> DOTTraits; 00072 typedef GraphTraits<GraphType> GTraits; 00073 typedef typename GTraits::NodeType NodeType; 00074 typedef typename GTraits::nodes_iterator node_iterator; 00075 typedef typename GTraits::ChildIteratorType child_iterator; 00076 public: 00077 GraphWriter(std::ostream &o, const GraphType &g) : O(o), G(g) {} 00078 00079 void writeHeader(const std::string &Name) { 00080 if (Name.empty()) 00081 O << "digraph foo {\n"; // Graph name doesn't matter 00082 else 00083 O << "digraph " << Name << " {\n"; 00084 00085 if (DOTTraits::renderGraphFromBottomUp()) 00086 O << "\trankdir=\"BT\";\n"; 00087 00088 std::string GraphName = DOTTraits::getGraphName(G); 00089 if (!GraphName.empty()) 00090 O << "\tlabel=\"" << DOT::EscapeString(GraphName) << "\";\n"; 00091 O << DOTTraits::getGraphProperties(G); 00092 O << "\n"; 00093 } 00094 00095 void writeFooter() { 00096 // Finish off the graph 00097 O << "}\n"; 00098 } 00099 00100 void writeNodes() { 00101 // Loop over the graph, printing it out... 00102 for (node_iterator I = GTraits::nodes_begin(G), E = GTraits::nodes_end(G); 00103 I != E; ++I) 00104 writeNode(&*I); 00105 } 00106 00107 void writeNode(NodeType *const *Node) { 00108 writeNode(*Node); 00109 } 00110 00111 void writeNode(NodeType *Node) { 00112 std::string NodeAttributes = DOTTraits::getNodeAttributes(Node); 00113 00114 O << "\tNode" << reinterpret_cast<const void*>(Node) << " [shape=record,"; 00115 if (!NodeAttributes.empty()) O << NodeAttributes << ","; 00116 O << "label=\"{"; 00117 00118 if (!DOTTraits::renderGraphFromBottomUp()) { 00119 O << DOT::EscapeString(DOTTraits::getNodeLabel(Node, G)); 00120 00121 // If we should include the address of the node in the label, do so now. 00122 if (DOTTraits::hasNodeAddressLabel(Node, G)) 00123 O << "|" << (void*)Node; 00124 } 00125 00126 // Print out the fields of the current node... 00127 child_iterator EI = GTraits::child_begin(Node); 00128 child_iterator EE = GTraits::child_end(Node); 00129 if (EI != EE) { 00130 if (!DOTTraits::renderGraphFromBottomUp()) O << "|"; 00131 O << "{"; 00132 00133 for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) { 00134 if (i) O << "|"; 00135 O << "<g" << i << ">" << DOTTraits::getEdgeSourceLabel(Node, EI); 00136 } 00137 00138 if (EI != EE) 00139 O << "|<g64>truncated..."; 00140 O << "}"; 00141 if (DOTTraits::renderGraphFromBottomUp()) O << "|"; 00142 } 00143 00144 if (DOTTraits::renderGraphFromBottomUp()) { 00145 O << DOT::EscapeString(DOTTraits::getNodeLabel(Node, G)); 00146 00147 // If we should include the address of the node in the label, do so now. 00148 if (DOTTraits::hasNodeAddressLabel(Node, G)) 00149 O << "|" << (void*)Node; 00150 } 00151 00152 O << "}\"];\n"; // Finish printing the "node" line 00153 00154 // Output all of the edges now 00155 EI = GTraits::child_begin(Node); 00156 for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) 00157 writeEdge(Node, i, EI); 00158 for (; EI != EE; ++EI) 00159 writeEdge(Node, 64, EI); 00160 } 00161 00162 void writeEdge(NodeType *Node, unsigned edgeidx, child_iterator EI) { 00163 if (NodeType *TargetNode = *EI) { 00164 int DestPort = -1; 00165 if (DOTTraits::edgeTargetsEdgeSource(Node, EI)) { 00166 child_iterator TargetIt = DOTTraits::getEdgeTarget(Node, EI); 00167 00168 // Figure out which edge this targets... 00169 unsigned Offset = std::distance(GTraits::child_begin(TargetNode), 00170 TargetIt); 00171 DestPort = static_cast<int>(Offset); 00172 } 00173 00174 emitEdge(reinterpret_cast<const void*>(Node), edgeidx, 00175 reinterpret_cast<const void*>(TargetNode), DestPort, 00176 DOTTraits::getEdgeAttributes(Node, EI)); 00177 } 00178 } 00179 00180 /// emitSimpleNode - Outputs a simple (non-record) node 00181 void emitSimpleNode(const void *ID, const std::string &Attr, 00182 const std::string &Label, unsigned NumEdgeSources = 0, 00183 const std::vector<std::string> *EdgeSourceLabels = 0) { 00184 O << "\tNode" << ID << "[ "; 00185 if (!Attr.empty()) 00186 O << Attr << ","; 00187 O << " label =\""; 00188 if (NumEdgeSources) O << "{"; 00189 O << DOT::EscapeString(Label); 00190 if (NumEdgeSources) { 00191 O << "|{"; 00192 00193 for (unsigned i = 0; i != NumEdgeSources; ++i) { 00194 if (i) O << "|"; 00195 O << "<g" << i << ">"; 00196 if (EdgeSourceLabels) O << (*EdgeSourceLabels)[i]; 00197 } 00198 O << "}}"; 00199 } 00200 O << "\"];\n"; 00201 } 00202 00203 /// emitEdge - Output an edge from a simple node into the graph... 00204 void emitEdge(const void *SrcNodeID, int SrcNodePort, 00205 const void *DestNodeID, int DestNodePort, 00206 const std::string &Attrs) { 00207 if (SrcNodePort > 64) return; // Eminating from truncated part? 00208 if (DestNodePort > 64) DestNodePort = 64; // Targetting the truncated part? 00209 00210 O << "\tNode" << SrcNodeID; 00211 if (SrcNodePort >= 0) 00212 O << ":g" << SrcNodePort; 00213 O << " -> Node" << reinterpret_cast<const void*>(DestNodeID); 00214 if (DestNodePort >= 0) 00215 O << ":g" << DestNodePort; 00216 00217 if (!Attrs.empty()) 00218 O << "[" << Attrs << "]"; 00219 O << ";\n"; 00220 } 00221 }; 00222 00223 template<typename GraphType> 00224 std::ostream &WriteGraph(std::ostream &O, const GraphType &G, 00225 const std::string &Name = "") { 00226 // Start the graph emission process... 00227 GraphWriter<GraphType> W(O, G); 00228 00229 // Output the header for the graph... 00230 W.writeHeader(Name); 00231 00232 // Emit all of the nodes in the graph... 00233 W.writeNodes(); 00234 00235 // Output any customizations on the graph 00236 DOTGraphTraits<GraphType>::addCustomGraphFeatures(G, W); 00237 00238 // Output the end of the graph 00239 W.writeFooter(); 00240 return O; 00241 } 00242 00243 template<typename GraphType> 00244 sys::Path WriteGraph(const GraphType &G, 00245 const std::string& Name, 00246 const std::string& Title = "") { 00247 sys::Path Filename = sys::Path::GetTemporaryDirectory();; 00248 Filename.appendComponent(Name + ".dot"); 00249 Filename.makeUnique(); 00250 std::cerr << "Writing '" << Filename << "'... "; 00251 00252 std::ofstream O(Filename.c_str()); 00253 00254 if (O.good()) { 00255 // Start the graph emission process... 00256 GraphWriter<GraphType> W(O, G); 00257 00258 // Output the header for the graph... 00259 W.writeHeader(Title); 00260 00261 // Emit all of the nodes in the graph... 00262 W.writeNodes(); 00263 00264 // Output any customizations on the graph 00265 DOTGraphTraits<GraphType>::addCustomGraphFeatures(G, W); 00266 00267 // Output the end of the graph 00268 W.writeFooter(); 00269 std::cerr << " done. \n"; 00270 00271 O.close(); 00272 00273 } else { 00274 std::cerr << "error opening file for writing!\n"; 00275 Filename.clear(); 00276 } 00277 00278 return Filename; 00279 } 00280 00281 /// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file, 00282 /// then cleanup. For use from the debugger. 00283 /// 00284 template<typename GraphType> 00285 void ViewGraph(const GraphType& G, 00286 const std::string& Name, 00287 const std::string& Title = "") { 00288 sys::Path Filename = WriteGraph(G, Name, Title); 00289 00290 if (Filename.isEmpty()) { 00291 return; 00292 } 00293 00294 DisplayGraph(Filename); 00295 } 00296 00297 } // End llvm namespace 00298 00299 #endif