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