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/ADT/Twine.h" 28 #include "llvm/Support/DOTGraphTraits.h" 29 #include "llvm/Support/raw_ostream.h" 30 #include <vector> 31 32 namespace llvm { 33 34 namespace DOT { // Private functions... 35 std::string EscapeString(const std::string &Label); 36 37 /// \brief Get a color string for this node number. Simply round-robin selects 38 /// from a reasonable number of colors. 39 StringRef getColorString(unsigned NodeNumber); 40 } 41 42 namespace GraphProgram { 43 enum Name { 44 DOT, 45 FDP, 46 NEATO, 47 TWOPI, 48 CIRCO 49 }; 50 } 51 52 bool DisplayGraph(StringRef Filename, bool wait = true, 53 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 "". getEdgeSourceLabels(raw_ostream & O,NodeType * Node)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: GraphWriter(raw_ostream & o,const GraphType & g,bool SN)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 writeHeader(const std::string & Title)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 writeFooter()134 void writeFooter() { 135 // Finish off the graph 136 O << "}\n"; 137 } 138 writeNodes()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 isNodeHidden(NodeType & Node)147 bool isNodeHidden(NodeType &Node) { 148 return isNodeHidden(&Node); 149 } 150 isNodeHidden(NodeType * const * Node)151 bool isNodeHidden(NodeType *const *Node) { 152 return isNodeHidden(*Node); 153 } 154 isNodeHidden(NodeType * Node)155 bool isNodeHidden(NodeType *Node) { 156 return DTraits.isNodeHidden(Node); 157 } 158 writeNode(NodeType & Node)159 void writeNode(NodeType& Node) { 160 writeNode(&Node); 161 } 162 writeNode(NodeType * const * Node)163 void writeNode(NodeType *const *Node) { 164 writeNode(*Node); 165 } 166 writeNode(NodeType * Node)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 std::string Id = DTraits.getNodeIdentifierLabel(Node, G); 179 if (!Id.empty()) 180 O << "|" << DOT::EscapeString(Id); 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 std::string Id = DTraits.getNodeIdentifierLabel(Node, G); 204 if (!Id.empty()) 205 O << "|" << DOT::EscapeString(Id); 206 207 std::string NodeDesc = DTraits.getNodeDescription(Node, G); 208 if (!NodeDesc.empty()) 209 O << "|" << DOT::EscapeString(NodeDesc); 210 } 211 212 if (DTraits.hasEdgeDestLabels()) { 213 O << "|{"; 214 215 unsigned i = 0, e = DTraits.numEdgeDestLabels(Node); 216 for (; i != e && i != 64; ++i) { 217 if (i) O << "|"; 218 O << "<d" << i << ">" 219 << DOT::EscapeString(DTraits.getEdgeDestLabel(Node, i)); 220 } 221 222 if (i != e) 223 O << "|<d64>truncated..."; 224 O << "}"; 225 } 226 227 O << "}\"];\n"; // Finish printing the "node" line 228 229 // Output all of the edges now 230 child_iterator EI = GTraits::child_begin(Node); 231 child_iterator EE = GTraits::child_end(Node); 232 for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) 233 if (!DTraits.isNodeHidden(*EI)) 234 writeEdge(Node, i, EI); 235 for (; EI != EE; ++EI) 236 if (!DTraits.isNodeHidden(*EI)) 237 writeEdge(Node, 64, EI); 238 } 239 writeEdge(NodeType * Node,unsigned edgeidx,child_iterator EI)240 void writeEdge(NodeType *Node, unsigned edgeidx, child_iterator EI) { 241 if (NodeType *TargetNode = *EI) { 242 int DestPort = -1; 243 if (DTraits.edgeTargetsEdgeSource(Node, EI)) { 244 child_iterator TargetIt = DTraits.getEdgeTarget(Node, EI); 245 246 // Figure out which edge this targets... 247 unsigned Offset = 248 (unsigned)std::distance(GTraits::child_begin(TargetNode), TargetIt); 249 DestPort = static_cast<int>(Offset); 250 } 251 252 if (DTraits.getEdgeSourceLabel(Node, EI).empty()) 253 edgeidx = -1; 254 255 emitEdge(static_cast<const void*>(Node), edgeidx, 256 static_cast<const void*>(TargetNode), DestPort, 257 DTraits.getEdgeAttributes(Node, EI, G)); 258 } 259 } 260 261 /// emitSimpleNode - Outputs a simple (non-record) node 262 void emitSimpleNode(const void *ID, const std::string &Attr, 263 const std::string &Label, unsigned NumEdgeSources = 0, 264 const std::vector<std::string> *EdgeSourceLabels = nullptr) { 265 O << "\tNode" << ID << "[ "; 266 if (!Attr.empty()) 267 O << Attr << ","; 268 O << " label =\""; 269 if (NumEdgeSources) O << "{"; 270 O << DOT::EscapeString(Label); 271 if (NumEdgeSources) { 272 O << "|{"; 273 274 for (unsigned i = 0; i != NumEdgeSources; ++i) { 275 if (i) O << "|"; 276 O << "<s" << i << ">"; 277 if (EdgeSourceLabels) O << DOT::EscapeString((*EdgeSourceLabels)[i]); 278 } 279 O << "}}"; 280 } 281 O << "\"];\n"; 282 } 283 284 /// 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)285 void emitEdge(const void *SrcNodeID, int SrcNodePort, 286 const void *DestNodeID, int DestNodePort, 287 const std::string &Attrs) { 288 if (SrcNodePort > 64) return; // Eminating from truncated part? 289 if (DestNodePort > 64) DestNodePort = 64; // Targeting the truncated part? 290 291 O << "\tNode" << SrcNodeID; 292 if (SrcNodePort >= 0) 293 O << ":s" << SrcNodePort; 294 O << " -> Node" << DestNodeID; 295 if (DestNodePort >= 0 && DTraits.hasEdgeDestLabels()) 296 O << ":d" << DestNodePort; 297 298 if (!Attrs.empty()) 299 O << "[" << Attrs << "]"; 300 O << ";\n"; 301 } 302 303 /// getOStream - Get the raw output stream into the graph file. Useful to 304 /// write fancy things using addCustomGraphFeatures(). getOStream()305 raw_ostream &getOStream() { 306 return O; 307 } 308 }; 309 310 template<typename GraphType> 311 raw_ostream &WriteGraph(raw_ostream &O, const GraphType &G, 312 bool ShortNames = false, 313 const Twine &Title = "") { 314 // Start the graph emission process... 315 GraphWriter<GraphType> W(O, G, ShortNames); 316 317 // Emit the graph. 318 W.writeGraph(Title.str()); 319 320 return O; 321 } 322 323 std::string createGraphFilename(const Twine &Name, int &FD); 324 325 template <typename GraphType> 326 std::string WriteGraph(const GraphType &G, const Twine &Name, 327 bool ShortNames = false, const Twine &Title = "") { 328 int FD; 329 // Windows can't always handle long paths, so limit the length of the name. 330 std::string N = Name.str(); 331 N = N.substr(0, std::min<std::size_t>(N.size(), 140)); 332 std::string Filename = createGraphFilename(N, FD); 333 raw_fd_ostream O(FD, /*shouldClose=*/ true); 334 335 if (FD == -1) { 336 errs() << "error opening file '" << Filename << "' for writing!\n"; 337 return ""; 338 } 339 340 llvm::WriteGraph(O, G, ShortNames, Title); 341 errs() << " done. \n"; 342 343 return Filename; 344 } 345 346 /// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file, 347 /// then cleanup. For use from the debugger. 348 /// 349 template<typename GraphType> 350 void ViewGraph(const GraphType &G, const Twine &Name, 351 bool ShortNames = false, const Twine &Title = "", 352 GraphProgram::Name Program = GraphProgram::DOT) { 353 std::string Filename = llvm::WriteGraph(G, Name, ShortNames, Title); 354 355 if (Filename.empty()) 356 return; 357 358 DisplayGraph(Filename, false, Program); 359 } 360 361 } // End llvm namespace 362 363 #endif 364