GraphWriter.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===- llvm/Support/GraphWriter.h - Write graph to a .dot file --*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // This file defines a simple interface that can be used to print out generic
  15. // LLVM graphs to ".dot" files. "dot" is a tool that is part of the AT&T
  16. // graphviz package (http://www.research.att.com/sw/tools/graphviz/) which can
  17. // be used to turn the files output by this interface into a variety of
  18. // different graphics formats.
  19. //
  20. // Graphs do not need to implement any interface past what is already required
  21. // by the GraphTraits template, but they can choose to implement specializations
  22. // of the DOTGraphTraits template if they want to customize the graphs output in
  23. // any way.
  24. //
  25. //===----------------------------------------------------------------------===//
  26. #ifndef LLVM_SUPPORT_GRAPHWRITER_H
  27. #define LLVM_SUPPORT_GRAPHWRITER_H
  28. #include "llvm/ADT/GraphTraits.h"
  29. #include "llvm/ADT/StringRef.h"
  30. #include "llvm/ADT/Twine.h"
  31. #include "llvm/Support/DOTGraphTraits.h"
  32. #include "llvm/Support/FileSystem.h"
  33. #include "llvm/Support/raw_ostream.h"
  34. #include <iterator>
  35. #include <string>
  36. #include <type_traits>
  37. #include <vector>
  38. namespace llvm {
  39. namespace DOT { // Private functions...
  40. std::string EscapeString(const std::string &Label);
  41. /// Get a color string for this node number. Simply round-robin selects
  42. /// from a reasonable number of colors.
  43. StringRef getColorString(unsigned NodeNumber);
  44. } // end namespace DOT
  45. namespace GraphProgram {
  46. enum Name {
  47. DOT,
  48. FDP,
  49. NEATO,
  50. TWOPI,
  51. CIRCO
  52. };
  53. } // end namespace GraphProgram
  54. bool DisplayGraph(StringRef Filename, bool wait = true,
  55. GraphProgram::Name program = GraphProgram::DOT);
  56. template<typename GraphType>
  57. class GraphWriter {
  58. raw_ostream &O;
  59. const GraphType &G;
  60. bool RenderUsingHTML = false;
  61. using DOTTraits = DOTGraphTraits<GraphType>;
  62. using GTraits = GraphTraits<GraphType>;
  63. using NodeRef = typename GTraits::NodeRef;
  64. using node_iterator = typename GTraits::nodes_iterator;
  65. using child_iterator = typename GTraits::ChildIteratorType;
  66. DOTTraits DTraits;
  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. // Writes the edge labels of the node to O and returns true if there are any
  72. // edge labels not equal to the empty string "".
  73. bool getEdgeSourceLabels(raw_ostream &O, NodeRef Node) {
  74. child_iterator EI = GTraits::child_begin(Node);
  75. child_iterator EE = GTraits::child_end(Node);
  76. bool hasEdgeSourceLabels = false;
  77. if (RenderUsingHTML)
  78. O << "</tr><tr>";
  79. for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) {
  80. std::string label = DTraits.getEdgeSourceLabel(Node, EI);
  81. if (label.empty())
  82. continue;
  83. hasEdgeSourceLabels = true;
  84. if (RenderUsingHTML)
  85. O << "<td colspan=\"1\" port=\"s" << i << "\">" << label << "</td>";
  86. else {
  87. if (i)
  88. O << "|";
  89. O << "<s" << i << ">" << DOT::EscapeString(label);
  90. }
  91. }
  92. if (EI != EE && hasEdgeSourceLabels) {
  93. if (RenderUsingHTML)
  94. O << "<td colspan=\"1\" port=\"s64\">truncated...</td>";
  95. else
  96. O << "|<s64>truncated...";
  97. }
  98. return hasEdgeSourceLabels;
  99. }
  100. public:
  101. GraphWriter(raw_ostream &o, const GraphType &g, bool SN) : O(o), G(g) {
  102. DTraits = DOTTraits(SN);
  103. RenderUsingHTML = DTraits.renderNodesUsingHTML();
  104. }
  105. void writeGraph(const std::string &Title = "") {
  106. // Output the header for the graph...
  107. writeHeader(Title);
  108. // Emit all of the nodes in the graph...
  109. writeNodes();
  110. // Output any customizations on the graph
  111. DOTGraphTraits<GraphType>::addCustomGraphFeatures(G, *this);
  112. // Output the end of the graph
  113. writeFooter();
  114. }
  115. void writeHeader(const std::string &Title) {
  116. std::string GraphName(DTraits.getGraphName(G));
  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. if (DTraits.renderGraphFromBottomUp())
  124. O << "\trankdir=\"BT\";\n";
  125. if (!Title.empty())
  126. O << "\tlabel=\"" << DOT::EscapeString(Title) << "\";\n";
  127. else if (!GraphName.empty())
  128. O << "\tlabel=\"" << DOT::EscapeString(GraphName) << "\";\n";
  129. O << DTraits.getGraphProperties(G);
  130. O << "\n";
  131. }
  132. void writeFooter() {
  133. // Finish off the graph
  134. O << "}\n";
  135. }
  136. void writeNodes() {
  137. // Loop over the graph, printing it out...
  138. for (const auto Node : nodes<GraphType>(G))
  139. if (!isNodeHidden(Node))
  140. writeNode(Node);
  141. }
  142. bool isNodeHidden(NodeRef Node) { return DTraits.isNodeHidden(Node, G); }
  143. void writeNode(NodeRef Node) {
  144. std::string NodeAttributes = DTraits.getNodeAttributes(Node, G);
  145. O << "\tNode" << static_cast<const void *>(Node) << " [shape=";
  146. if (RenderUsingHTML)
  147. O << "none,";
  148. else
  149. O << "record,";
  150. if (!NodeAttributes.empty()) O << NodeAttributes << ",";
  151. O << "label=";
  152. if (RenderUsingHTML) {
  153. // Count the numbewr of edges out of the node to determine how
  154. // many columns to span (max 64)
  155. unsigned ColSpan = 0;
  156. child_iterator EI = GTraits::child_begin(Node);
  157. child_iterator EE = GTraits::child_end(Node);
  158. for (; EI != EE && ColSpan != 64; ++EI, ++ColSpan)
  159. ;
  160. if (ColSpan == 0)
  161. ColSpan = 1;
  162. // Include truncated messages when counting.
  163. if (EI != EE)
  164. ++ColSpan;
  165. O << "<<table border=\"0\" cellborder=\"1\" cellspacing=\"0\""
  166. << " cellpadding=\"0\"><tr><td align=\"text\" colspan=\"" << ColSpan
  167. << "\">";
  168. } else
  169. O << "\"{";
  170. if (!DTraits.renderGraphFromBottomUp()) {
  171. if (RenderUsingHTML)
  172. O << DTraits.getNodeLabel(Node, G) << "</td>";
  173. else
  174. O << DOT::EscapeString(DTraits.getNodeLabel(Node, G));
  175. // If we should include the address of the node in the label, do so now.
  176. std::string Id = DTraits.getNodeIdentifierLabel(Node, G);
  177. if (!Id.empty())
  178. O << "|" << DOT::EscapeString(Id);
  179. std::string NodeDesc = DTraits.getNodeDescription(Node, G);
  180. if (!NodeDesc.empty())
  181. O << "|" << DOT::EscapeString(NodeDesc);
  182. }
  183. std::string edgeSourceLabels;
  184. raw_string_ostream EdgeSourceLabels(edgeSourceLabels);
  185. bool hasEdgeSourceLabels = getEdgeSourceLabels(EdgeSourceLabels, Node);
  186. if (hasEdgeSourceLabels) {
  187. if (!DTraits.renderGraphFromBottomUp())
  188. if (!RenderUsingHTML)
  189. O << "|";
  190. if (RenderUsingHTML)
  191. O << EdgeSourceLabels.str();
  192. else
  193. O << "{" << EdgeSourceLabels.str() << "}";
  194. if (DTraits.renderGraphFromBottomUp())
  195. if (!RenderUsingHTML)
  196. O << "|";
  197. }
  198. if (DTraits.renderGraphFromBottomUp()) {
  199. if (RenderUsingHTML)
  200. O << DTraits.getNodeLabel(Node, G);
  201. else
  202. O << DOT::EscapeString(DTraits.getNodeLabel(Node, G));
  203. // If we should include the address of the node in the label, do so now.
  204. std::string Id = DTraits.getNodeIdentifierLabel(Node, G);
  205. if (!Id.empty())
  206. O << "|" << DOT::EscapeString(Id);
  207. std::string NodeDesc = DTraits.getNodeDescription(Node, G);
  208. if (!NodeDesc.empty())
  209. O << "|" << DOT::EscapeString(NodeDesc);
  210. }
  211. if (DTraits.hasEdgeDestLabels()) {
  212. O << "|{";
  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. if (i != e)
  220. O << "|<d64>truncated...";
  221. O << "}";
  222. }
  223. if (RenderUsingHTML)
  224. O << "</tr></table>>";
  225. else
  226. O << "}\"";
  227. O << "];\n"; // Finish printing the "node" line
  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, G))
  233. writeEdge(Node, i, EI);
  234. for (; EI != EE; ++EI)
  235. if (!DTraits.isNodeHidden(*EI, G))
  236. writeEdge(Node, 64, EI);
  237. }
  238. void writeEdge(NodeRef Node, unsigned edgeidx, child_iterator EI) {
  239. if (NodeRef TargetNode = *EI) {
  240. int DestPort = -1;
  241. if (DTraits.edgeTargetsEdgeSource(Node, EI)) {
  242. child_iterator TargetIt = DTraits.getEdgeTarget(Node, EI);
  243. // Figure out which edge this targets...
  244. unsigned Offset =
  245. (unsigned)std::distance(GTraits::child_begin(TargetNode), TargetIt);
  246. DestPort = static_cast<int>(Offset);
  247. }
  248. if (DTraits.getEdgeSourceLabel(Node, EI).empty())
  249. edgeidx = -1;
  250. emitEdge(static_cast<const void*>(Node), edgeidx,
  251. static_cast<const void*>(TargetNode), DestPort,
  252. DTraits.getEdgeAttributes(Node, EI, G));
  253. }
  254. }
  255. /// emitSimpleNode - Outputs a simple (non-record) node
  256. void emitSimpleNode(const void *ID, const std::string &Attr,
  257. const std::string &Label, unsigned NumEdgeSources = 0,
  258. const std::vector<std::string> *EdgeSourceLabels = nullptr) {
  259. O << "\tNode" << ID << "[ ";
  260. if (!Attr.empty())
  261. O << Attr << ",";
  262. O << " label =\"";
  263. if (NumEdgeSources) O << "{";
  264. O << DOT::EscapeString(Label);
  265. if (NumEdgeSources) {
  266. O << "|{";
  267. for (unsigned i = 0; i != NumEdgeSources; ++i) {
  268. if (i) O << "|";
  269. O << "<s" << i << ">";
  270. if (EdgeSourceLabels) O << DOT::EscapeString((*EdgeSourceLabels)[i]);
  271. }
  272. O << "}}";
  273. }
  274. O << "\"];\n";
  275. }
  276. /// emitEdge - Output an edge from a simple node into the graph...
  277. void emitEdge(const void *SrcNodeID, int SrcNodePort,
  278. const void *DestNodeID, int DestNodePort,
  279. const std::string &Attrs) {
  280. if (SrcNodePort > 64) return; // Eminating from truncated part?
  281. if (DestNodePort > 64) DestNodePort = 64; // Targeting the truncated part?
  282. O << "\tNode" << SrcNodeID;
  283. if (SrcNodePort >= 0)
  284. O << ":s" << SrcNodePort;
  285. O << " -> Node" << DestNodeID;
  286. if (DestNodePort >= 0 && DTraits.hasEdgeDestLabels())
  287. O << ":d" << DestNodePort;
  288. if (!Attrs.empty())
  289. O << "[" << Attrs << "]";
  290. O << ";\n";
  291. }
  292. /// getOStream - Get the raw output stream into the graph file. Useful to
  293. /// write fancy things using addCustomGraphFeatures().
  294. raw_ostream &getOStream() {
  295. return O;
  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. // Emit the graph.
  305. W.writeGraph(Title.str());
  306. return O;
  307. }
  308. std::string createGraphFilename(const Twine &Name, int &FD);
  309. /// Writes graph into a provided @c Filename.
  310. /// If @c Filename is empty, generates a random one.
  311. /// \return The resulting filename, or an empty string if writing
  312. /// failed.
  313. template <typename GraphType>
  314. std::string WriteGraph(const GraphType &G, const Twine &Name,
  315. bool ShortNames = false,
  316. const Twine &Title = "",
  317. std::string Filename = "") {
  318. int FD;
  319. if (Filename.empty()) {
  320. Filename = createGraphFilename(Name.str(), FD);
  321. } else {
  322. std::error_code EC = sys::fs::openFileForWrite(
  323. Filename, FD, sys::fs::CD_CreateAlways, sys::fs::OF_Text);
  324. // Writing over an existing file is not considered an error.
  325. if (EC == std::errc::file_exists) {
  326. errs() << "file exists, overwriting" << "\n";
  327. } else if (EC) {
  328. errs() << "error writing into file" << "\n";
  329. return "";
  330. } else {
  331. errs() << "writing to the newly created file " << Filename << "\n";
  332. }
  333. }
  334. raw_fd_ostream O(FD, /*shouldClose=*/ true);
  335. if (FD == -1) {
  336. errs() << "error opening file '" << Filename << "' for writing!\n";
  337. return "";
  338. }
  339. llvm::WriteGraph(O, G, ShortNames, Title);
  340. errs() << " done. \n";
  341. return Filename;
  342. }
  343. /// DumpDotGraph - Just dump a dot graph to the user-provided file name.
  344. #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  345. template <typename GraphType>
  346. LLVM_DUMP_METHOD void
  347. dumpDotGraphToFile(const GraphType &G, const Twine &FileName,
  348. const Twine &Title, bool ShortNames = false,
  349. const Twine &Name = "") {
  350. llvm::WriteGraph(G, Name, ShortNames, Title, FileName.str());
  351. }
  352. #endif
  353. /// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file,
  354. /// then cleanup. For use from the debugger.
  355. ///
  356. template<typename GraphType>
  357. void ViewGraph(const GraphType &G, const Twine &Name,
  358. bool ShortNames = false, const Twine &Title = "",
  359. GraphProgram::Name Program = GraphProgram::DOT) {
  360. std::string Filename = llvm::WriteGraph(G, Name, ShortNames, Title);
  361. if (Filename.empty())
  362. return;
  363. DisplayGraph(Filename, false, Program);
  364. }
  365. } // end namespace llvm
  366. #endif // LLVM_SUPPORT_GRAPHWRITER_H
  367. #ifdef __GNUC__
  368. #pragma GCC diagnostic pop
  369. #endif