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