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