From 3fc275df6c6729a0e5bf1f1e2ed662cf6a387a4f Mon Sep 17 00:00:00 2001 From: Diogo Sousa Date: Sat, 13 Oct 2018 15:43:12 +0100 Subject: [PATCH] Added graphviz visualization for obligation forests. This can be a big help when debugging the trait resolver. --- src/Cargo.lock | 1 + src/librustc_data_structures/Cargo.toml | 1 + src/librustc_data_structures/lib.rs | 1 + .../obligation_forest/graphviz.rs | 101 ++++++++++++++++++ .../obligation_forest/mod.rs | 2 + 5 files changed, 106 insertions(+) create mode 100644 src/librustc_data_structures/obligation_forest/graphviz.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 52314b0ac89..d519522a1b0 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -2161,6 +2161,7 @@ version = "0.0.0" dependencies = [ "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "graphviz 0.0.0", "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 5a72fde6a2c..10820007629 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -13,6 +13,7 @@ ena = "0.9.3" log = "0.4" rustc_cratesio_shim = { path = "../librustc_cratesio_shim" } serialize = { path = "../libserialize" } +graphviz = { path = "../libgraphviz" } cfg-if = "0.1.2" stable_deref_trait = "1.0.0" parking_lot_core = "0.2.8" diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index c592a5eb1e0..5b01892dcb3 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -49,6 +49,7 @@ extern crate rustc_rayon as rayon; extern crate rustc_rayon_core as rayon_core; extern crate rustc_hash; extern crate serialize; +extern crate graphviz; extern crate smallvec; // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. diff --git a/src/librustc_data_structures/obligation_forest/graphviz.rs b/src/librustc_data_structures/obligation_forest/graphviz.rs new file mode 100644 index 00000000000..dcd448ee44f --- /dev/null +++ b/src/librustc_data_structures/obligation_forest/graphviz.rs @@ -0,0 +1,101 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use graphviz as dot; +use obligation_forest::{ForestObligation, ObligationForest}; +use std::env::var_os; +use std::fs::File; +use std::path::Path; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +impl ObligationForest { + /// Create a graphviz representation of the obligation forest. Given a directory this will + /// create files with name of the format `_.gv`. The counter is + /// global and is maintained internally. + /// + /// Calling this will do nothing unless the environment variable + /// `DUMP_OBLIGATION_FOREST_GRAPHVIZ` is defined. + /// + /// A few post-processing that you might want to do make the forest easier to visualize: + /// + /// * `sed 's,std::[a-z]*::,,g'` — Deletes the `std::::` prefix of paths. + /// * `sed 's,"Binder(TraitPredicate(<\(.*\)>)) (\([^)]*\))","\1 (\2)",'` — Transforms + /// `Binder(TraitPredicate())` into just ``. + #[allow(dead_code)] + pub fn dump_graphviz>(&self, dir: P, description: &str) { + static COUNTER: AtomicUsize = AtomicUsize::new(0); + + if var_os("DUMP_OBLIGATION_FOREST_GRAPHVIZ").is_none() { + return; + } + + let counter = COUNTER.fetch_add(1, Ordering::AcqRel); + + let file_path = dir.as_ref().join(format!("{:010}_{}.gv", counter, description)); + + let mut gv_file = File::create(file_path).unwrap(); + + dot::render(&self, &mut gv_file).unwrap(); + } +} + +impl<'a, O: ForestObligation + 'a> dot::Labeller<'a> for &'a ObligationForest { + type Node = usize; + type Edge = (usize, usize); + + fn graph_id(&self) -> dot::Id { + dot::Id::new("trait_obligation_forest").unwrap() + } + + fn node_id(&self, index: &Self::Node) -> dot::Id { + dot::Id::new(format!("obligation_{}", index)).unwrap() + } + + fn node_label(&self, index: &Self::Node) -> dot::LabelText { + let node = &self.nodes[*index]; + let label = format!("{:?} ({:?})", node.obligation.as_predicate(), node.state.get()); + + dot::LabelText::LabelStr(label.into()) + } + + fn edge_label(&self, (_index_source, _index_target): &Self::Edge) -> dot::LabelText { + dot::LabelText::LabelStr("".into()) + } +} + +impl<'a, O: ForestObligation + 'a> dot::GraphWalk<'a> for &'a ObligationForest { + type Node = usize; + type Edge = (usize, usize); + + fn nodes(&self) -> dot::Nodes { + (0..self.nodes.len()).collect() + } + + fn edges(&self) -> dot::Edges { + (0..self.nodes.len()) + .flat_map(|i| { + let node = &self.nodes[i]; + + node.parent.iter().map(|p| p.get()) + .chain(node.dependents.iter().map(|p| p.get())) + .map(move |p| (p, i)) + }) + .collect() + } + + fn source(&self, (s, _): &Self::Edge) -> Self::Node { + *s + } + + fn target(&self, (_, t): &Self::Edge) -> Self::Node { + *t + } +} diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index f159857e744..92cc398db50 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -26,6 +26,8 @@ use std::marker::PhantomData; mod node_index; use self::node_index::NodeIndex; +mod graphviz; + #[cfg(test)] mod test;