1 use rustc_data_structures::graph::{self, iterate};
2 use rustc_graphviz as dot;
3 use rustc_middle::ty::TyCtxt;
4 use std::io::{self, Write};
6 pub struct GraphvizWriter<
8 G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
9 NodeContentFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
10 EdgeLabelsFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
14 graphviz_name: String,
15 graph_label: Option<String>,
16 node_content_fn: NodeContentFn,
17 edge_labels_fn: EdgeLabelsFn,
22 G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
23 NodeContentFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
24 EdgeLabelsFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
25 > GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn>
30 node_content_fn: NodeContentFn,
31 edge_labels_fn: EdgeLabelsFn,
36 graphviz_name: graphviz_name.to_owned(),
46 node_content_fn: NodeContentFn,
47 edge_labels_fn: EdgeLabelsFn,
52 graphviz_name: graphviz_name.to_owned(),
59 pub fn set_graph_label(&mut self, graph_label: &str) {
60 self.graph_label = Some(graph_label.to_owned());
63 /// Write a graphviz DOT of the graph
64 pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()>
68 let kind = if self.is_subgraph { "subgraph" } else { "digraph" };
69 let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Print border around graph
70 // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
71 // prepend "Mir_" to the graphviz_safe_def_name(def_id)
72 writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?;
74 // Global graph properties
75 let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
76 let mut graph_attrs = vec![&font[..]];
77 let mut content_attrs = vec![&font[..]];
79 let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
81 graph_attrs.push(r#"bgcolor="black""#);
82 graph_attrs.push(r#"fontcolor="white""#);
83 content_attrs.push(r#"color="white""#);
84 content_attrs.push(r#"fontcolor="white""#);
87 writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?;
88 let content_attrs_str = content_attrs.join(" ");
89 writeln!(w, r#" node [{}];"#, content_attrs_str)?;
90 writeln!(w, r#" edge [{}];"#, content_attrs_str)?;
93 if let Some(graph_label) = &self.graph_label {
94 self.write_graph_label(graph_label, w)?;
98 for node in iterate::post_order_from(self.graph, self.graph.start_node()) {
99 self.write_node(node, dark_mode, w)?;
103 for source in iterate::post_order_from(self.graph, self.graph.start_node()) {
104 self.write_edges(source, w)?;
109 /// Write a graphviz DOT node for the given node.
110 pub fn write_node<W>(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()>
114 // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
115 write!(w, r#" {} [shape="none", label=<"#, self.node(node))?;
117 write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
119 // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
120 // we need generic way to know if node header should have a different color. For example,
123 // let (blk, bgcolor) = if data.is_cleanup {
124 // let color = if dark_mode { "royalblue" } else { "lightblue" };
125 // (format!("{:?} (cleanup)", node), color)
127 // let color = if dark_mode { "dimgray" } else { "gray" };
128 // (format!("{:?}", node), color)
130 let color = if dark_mode { "dimgray" } else { "gray" };
131 let (blk, bgcolor) = (format!("{:?}", node), color);
134 r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
135 attrs = r#"align="center""#,
141 for section in (self.node_content_fn)(node) {
144 r#"<tr><td align="left" balign="left">{}</td></tr>"#,
145 dot::escape_html(§ion).replace("\n", "<br/>")
150 write!(w, "</table>")?;
152 // Close the node label and the node itself.
156 /// Write graphviz DOT edges with labels between the given node and all of its successors.
157 fn write_edges<W>(&self, source: G::Node, w: &mut W) -> io::Result<()>
161 let edge_labels = (self.edge_labels_fn)(source);
162 for (index, target) in self.graph.successors(source).enumerate() {
163 let src = self.node(source);
164 let trg = self.node(target);
165 let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) {
166 dot::escape_html(edge_label).replace("\n", r#"<br align="left"/>"#)
170 writeln!(w, r#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?;
175 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
176 /// will appear below the graph.
177 fn write_graph_label<W>(&self, label: &str, w: &mut W) -> io::Result<()>
181 let lines = label.split('\n').map(|s| dot::escape_html(s)).collect::<Vec<_>>();
182 let escaped_label = lines.join(r#"<br align="left"/>"#);
183 writeln!(w, r#" label=<<br/><br/>{}<br align="left"/><br/><br/><br/>>;"#, escaped_label)
186 fn node(&self, node: G::Node) -> String {
187 format!("{:?}__{}", node, self.graphviz_name)