Skip to content

Commit bef62cc

Browse files
committed
Auto merge of #54486 - orium:obligation-forest-graphviz, r=nikomatsakis
Added graphviz visualization for obligation forests. This can be a big help when debugging the trait resolver.
2 parents 8a7048b + 3fc275d commit bef62cc

File tree

5 files changed

+106
-0
lines changed

5 files changed

+106
-0
lines changed

Diff for: src/Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -2161,6 +2161,7 @@ version = "0.0.0"
21612161
dependencies = [
21622162
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
21632163
"ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
2164+
"graphviz 0.0.0",
21642165
"log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
21652166
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
21662167
"parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",

Diff for: src/librustc_data_structures/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ena = "0.9.3"
1313
log = "0.4"
1414
rustc_cratesio_shim = { path = "../librustc_cratesio_shim" }
1515
serialize = { path = "../libserialize" }
16+
graphviz = { path = "../libgraphviz" }
1617
cfg-if = "0.1.2"
1718
stable_deref_trait = "1.0.0"
1819
parking_lot_core = "0.2.8"

Diff for: src/librustc_data_structures/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ extern crate rustc_rayon as rayon;
4949
extern crate rustc_rayon_core as rayon_core;
5050
extern crate rustc_hash;
5151
extern crate serialize;
52+
extern crate graphviz;
5253
extern crate smallvec;
5354

5455
// See librustc_cratesio_shim/Cargo.toml for a comment explaining this.
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use graphviz as dot;
12+
use obligation_forest::{ForestObligation, ObligationForest};
13+
use std::env::var_os;
14+
use std::fs::File;
15+
use std::path::Path;
16+
use std::sync::atomic::AtomicUsize;
17+
use std::sync::atomic::Ordering;
18+
19+
impl<O: ForestObligation> ObligationForest<O> {
20+
/// Create a graphviz representation of the obligation forest. Given a directory this will
21+
/// create files with name of the format `<counter>_<description>.gv`. The counter is
22+
/// global and is maintained internally.
23+
///
24+
/// Calling this will do nothing unless the environment variable
25+
/// `DUMP_OBLIGATION_FOREST_GRAPHVIZ` is defined.
26+
///
27+
/// A few post-processing that you might want to do make the forest easier to visualize:
28+
///
29+
/// * `sed 's,std::[a-z]*::,,g'` — Deletes the `std::<package>::` prefix of paths.
30+
/// * `sed 's,"Binder(TraitPredicate(<\(.*\)>)) (\([^)]*\))","\1 (\2)",'` — Transforms
31+
/// `Binder(TraitPredicate(<predicate>))` into just `<predicate>`.
32+
#[allow(dead_code)]
33+
pub fn dump_graphviz<P: AsRef<Path>>(&self, dir: P, description: &str) {
34+
static COUNTER: AtomicUsize = AtomicUsize::new(0);
35+
36+
if var_os("DUMP_OBLIGATION_FOREST_GRAPHVIZ").is_none() {
37+
return;
38+
}
39+
40+
let counter = COUNTER.fetch_add(1, Ordering::AcqRel);
41+
42+
let file_path = dir.as_ref().join(format!("{:010}_{}.gv", counter, description));
43+
44+
let mut gv_file = File::create(file_path).unwrap();
45+
46+
dot::render(&self, &mut gv_file).unwrap();
47+
}
48+
}
49+
50+
impl<'a, O: ForestObligation + 'a> dot::Labeller<'a> for &'a ObligationForest<O> {
51+
type Node = usize;
52+
type Edge = (usize, usize);
53+
54+
fn graph_id(&self) -> dot::Id {
55+
dot::Id::new("trait_obligation_forest").unwrap()
56+
}
57+
58+
fn node_id(&self, index: &Self::Node) -> dot::Id {
59+
dot::Id::new(format!("obligation_{}", index)).unwrap()
60+
}
61+
62+
fn node_label(&self, index: &Self::Node) -> dot::LabelText {
63+
let node = &self.nodes[*index];
64+
let label = format!("{:?} ({:?})", node.obligation.as_predicate(), node.state.get());
65+
66+
dot::LabelText::LabelStr(label.into())
67+
}
68+
69+
fn edge_label(&self, (_index_source, _index_target): &Self::Edge) -> dot::LabelText {
70+
dot::LabelText::LabelStr("".into())
71+
}
72+
}
73+
74+
impl<'a, O: ForestObligation + 'a> dot::GraphWalk<'a> for &'a ObligationForest<O> {
75+
type Node = usize;
76+
type Edge = (usize, usize);
77+
78+
fn nodes(&self) -> dot::Nodes<Self::Node> {
79+
(0..self.nodes.len()).collect()
80+
}
81+
82+
fn edges(&self) -> dot::Edges<Self::Edge> {
83+
(0..self.nodes.len())
84+
.flat_map(|i| {
85+
let node = &self.nodes[i];
86+
87+
node.parent.iter().map(|p| p.get())
88+
.chain(node.dependents.iter().map(|p| p.get()))
89+
.map(move |p| (p, i))
90+
})
91+
.collect()
92+
}
93+
94+
fn source(&self, (s, _): &Self::Edge) -> Self::Node {
95+
*s
96+
}
97+
98+
fn target(&self, (_, t): &Self::Edge) -> Self::Node {
99+
*t
100+
}
101+
}

Diff for: src/librustc_data_structures/obligation_forest/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ use std::marker::PhantomData;
2626
mod node_index;
2727
use self::node_index::NodeIndex;
2828

29+
mod graphviz;
30+
2931
#[cfg(test)]
3032
mod test;
3133

0 commit comments

Comments
 (0)