Merge pull request #122 from matklad/debug

allow to peek at values via debug query interface
This commit is contained in:
Niko Matsakis 2019-01-25 05:13:27 -05:00 committed by GitHub
commit 3d1f9dac2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 89 additions and 42 deletions

View file

@ -14,15 +14,38 @@ pub trait DebugQueryTable {
/// Key of this query. /// Key of this query.
type Key; type Key;
/// Value of this query.
type Value;
/// True if salsa thinks that the value for `key` is a /// True if salsa thinks that the value for `key` is a
/// **constant**, meaning that it can never change, no matter what /// **constant**, meaning that it can never change, no matter what
/// values the inputs take on from this point. /// values the inputs take on from this point.
fn is_constant(&self, key: Self::Key) -> bool; fn is_constant(&self, key: Self::Key) -> bool;
/// Get the (current) set of the keys in the query table. /// Get the (current) set of the entries in the query table.
fn keys<C>(&self) -> C fn entries<C>(&self) -> C
where where
C: FromIterator<Self::Key>; C: FromIterator<TableEntry<Self::Key, Self::Value>>;
}
/// An entry from a query table, for debugging and inspecting the table state.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct TableEntry<K, V> {
/// key of the query
pub key: K,
/// value of the query, if it is stored
pub value: Option<V>,
_for_future_use: (),
}
impl<K, V> TableEntry<K, V> {
pub(crate) fn new(key: K, value: Option<V>) -> TableEntry<K, V> {
TableEntry {
key,
value,
_for_future_use: (),
}
}
} }
impl<DB, Q> DebugQueryTable for QueryTable<'_, DB, Q> impl<DB, Q> DebugQueryTable for QueryTable<'_, DB, Q>
@ -31,15 +54,16 @@ where
Q: Query<DB>, Q: Query<DB>,
{ {
type Key = Q::Key; type Key = Q::Key;
type Value = Q::Value;
fn is_constant(&self, key: Q::Key) -> bool { fn is_constant(&self, key: Q::Key) -> bool {
self.storage.is_constant(self.db, &key) self.storage.is_constant(self.db, &key)
} }
fn keys<C>(&self) -> C fn entries<C>(&self) -> C
where where
C: FromIterator<Q::Key>, C: FromIterator<TableEntry<Self::Key, Self::Value>>,
{ {
self.storage.keys(self.db) self.storage.entries(self.db)
} }
} }

View file

@ -1,3 +1,4 @@
use crate::debug::TableEntry;
use crate::plumbing::CycleDetected; use crate::plumbing::CycleDetected;
use crate::plumbing::DatabaseKey; use crate::plumbing::DatabaseKey;
use crate::plumbing::QueryFunction; use crate::plumbing::QueryFunction;
@ -162,6 +163,13 @@ where
waiting: Default::default(), waiting: Default::default(),
} }
} }
fn value(&self) -> Option<Q::Value> {
match self {
QueryState::InProgress { .. } => None,
QueryState::Memoized(memo) => memo.value.clone(),
}
}
} }
struct Memo<DB, Q> struct Memo<DB, Q>
@ -915,12 +923,14 @@ where
} }
} }
fn keys<C>(&self, _db: &DB) -> C fn entries<C>(&self, _db: &DB) -> C
where where
C: std::iter::FromIterator<Q::Key>, C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
{ {
let map = self.map.read(); let map = self.map.read();
map.keys().cloned().collect() map.iter()
.map(|(key, query_state)| TableEntry::new(key.clone(), query_state.value()))
.collect()
} }
} }

View file

@ -1,3 +1,4 @@
use crate::debug::TableEntry;
use crate::plumbing::CycleDetected; use crate::plumbing::CycleDetected;
use crate::plumbing::InputQueryStorageOps; use crate::plumbing::InputQueryStorageOps;
use crate::plumbing::QueryStorageMassOps; use crate::plumbing::QueryStorageMassOps;
@ -194,12 +195,16 @@ where
.unwrap_or(false) .unwrap_or(false)
} }
fn keys<C>(&self, _db: &DB) -> C fn entries<C>(&self, _db: &DB) -> C
where where
C: std::iter::FromIterator<Q::Key>, C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
{ {
let map = self.map.read(); let map = self.map.read();
map.keys().cloned().collect() map.iter()
.map(|(key, stamped_value)| {
TableEntry::new(key.clone(), Some(stamped_value.value.clone()))
})
.collect()
} }
} }

View file

@ -1,5 +1,6 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use crate::debug::TableEntry;
use crate::Database; use crate::Database;
use crate::Query; use crate::Query;
use crate::QueryTable; use crate::QueryTable;
@ -176,10 +177,10 @@ where
/// Check if `key` is (currently) believed to be a constant. /// Check if `key` is (currently) believed to be a constant.
fn is_constant(&self, db: &DB, key: &Q::Key) -> bool; fn is_constant(&self, db: &DB, key: &Q::Key) -> bool;
/// Get the (current) set of the keys in the query storage /// Get the (current) set of the entries in the query storage
fn keys<C>(&self, db: &DB) -> C fn entries<C>(&self, db: &DB) -> C
where where
C: std::iter::FromIterator<Q::Key>; C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>;
} }
/// An optional trait that is implemented for "user mutable" storage: /// An optional trait that is implemented for "user mutable" storage:

View file

@ -3,16 +3,6 @@ use crate::group::*;
use salsa::debug::DebugQueryTable; use salsa::debug::DebugQueryTable;
use salsa::{Database, SweepStrategy}; use salsa::{Database, SweepStrategy};
macro_rules! assert_keys {
($db:expr, $($query:expr => ($($key:expr),*),)*) => {
$(
let mut keys = $db.query($query).keys::<Vec<_>>();
keys.sort();
assert_eq!(keys, vec![$($key),*], "query {:?} had wrong keys", $query);
)*
};
}
#[test] #[test]
fn compute_one() { fn compute_one() {
let mut db = db::DatabaseImpl::default(); let mut db = db::DatabaseImpl::default();

View file

@ -9,7 +9,7 @@ fn sweep_default() {
db.fibonacci(5); db.fibonacci(5);
let k: Vec<_> = db.query(FibonacciQuery).keys(); let k: Vec<_> = db.query(FibonacciQuery).entries();
assert_eq!(k.len(), 6); assert_eq!(k.len(), 6);
db.salsa_runtime().next_revision(); db.salsa_runtime().next_revision();
@ -20,9 +20,10 @@ fn sweep_default() {
// fibonacci is a constant, so it will not be invalidated, // fibonacci is a constant, so it will not be invalidated,
// hence we keep 3 and 5 but remove the rest. // hence we keep 3 and 5 but remove the rest.
db.sweep_all(SweepStrategy::default()); db.sweep_all(SweepStrategy::default());
let mut k: Vec<_> = db.query(FibonacciQuery).keys(); assert_keys! {
k.sort(); db,
assert_eq!(k, vec![3, 5]); FibonacciQuery => (3, 5),
}
// Even though we ran the sweep, 5 is still in cache // Even though we ran the sweep, 5 is still in cache
db.clear_log(); db.clear_log();
@ -31,9 +32,11 @@ fn sweep_default() {
// Same but we discard values this time. // Same but we discard values this time.
db.sweep_all(SweepStrategy::default().discard_values()); db.sweep_all(SweepStrategy::default().discard_values());
let mut k: Vec<_> = db.query(FibonacciQuery).keys(); db.sweep_all(SweepStrategy::default());
k.sort(); assert_keys! {
assert_eq!(k, vec![3, 5]); db,
FibonacciQuery => (3, 5),
}
// Now we have to recompute // Now we have to recompute
db.clear_log(); db.clear_log();

View file

@ -1,3 +1,14 @@
macro_rules! assert_keys {
($db:expr, $($query:expr => ($($key:expr),*),)*) => {
$(
let entries = $db.query($query).entries::<Vec<_>>();
let mut keys = entries.into_iter().map(|e| e.key).collect::<Vec<_>>();
keys.sort();
assert_eq!(keys, vec![$($key),*], "query {:?} had wrong keys", $query);
)*
};
}
mod db; mod db;
mod derived_tests; mod derived_tests;
mod discard_values; mod discard_values;

View file

@ -13,7 +13,7 @@ fn one_rev() {
db.fibonacci(5); db.fibonacci(5);
let k: Vec<_> = db.query(FibonacciQuery).keys(); let k: Vec<_> = db.query(FibonacciQuery).entries();
assert_eq!(k.len(), 6); assert_eq!(k.len(), 6);
// Everything was used in this revision, so // Everything was used in this revision, so
@ -28,7 +28,7 @@ fn two_rev_nothing() {
db.fibonacci(5); db.fibonacci(5);
let k: Vec<_> = db.query(FibonacciQuery).keys(); let k: Vec<_> = db.query(FibonacciQuery).entries();
assert_eq!(k.len(), 6); assert_eq!(k.len(), 6);
db.salsa_runtime().next_revision(); db.salsa_runtime().next_revision();
@ -37,7 +37,7 @@ fn two_rev_nothing() {
// everything gets collected. // everything gets collected.
db.sweep_all(SweepStrategy::default()); db.sweep_all(SweepStrategy::default());
let k: Vec<_> = db.query(FibonacciQuery).keys(); let k: Vec<_> = db.query(FibonacciQuery).entries();
assert_eq!(k.len(), 0); assert_eq!(k.len(), 0);
} }
@ -47,7 +47,7 @@ fn two_rev_one_use() {
db.fibonacci(5); db.fibonacci(5);
let k: Vec<_> = db.query(FibonacciQuery).keys(); let k: Vec<_> = db.query(FibonacciQuery).entries();
assert_eq!(k.len(), 6); assert_eq!(k.len(), 6);
db.salsa_runtime().next_revision(); db.salsa_runtime().next_revision();
@ -58,8 +58,10 @@ fn two_rev_one_use() {
// hence we keep `fibonacci(5)` but remove 0..=4. // hence we keep `fibonacci(5)` but remove 0..=4.
db.sweep_all(SweepStrategy::default()); db.sweep_all(SweepStrategy::default());
let k: Vec<_> = db.query(FibonacciQuery).keys(); assert_keys! {
assert_eq!(k, vec![5]); db,
FibonacciQuery => (5),
}
} }
#[test] #[test]
@ -68,7 +70,7 @@ fn two_rev_two_uses() {
db.fibonacci(5); db.fibonacci(5);
let k: Vec<_> = db.query(FibonacciQuery).keys(); let k: Vec<_> = db.query(FibonacciQuery).entries();
assert_eq!(k.len(), 6); assert_eq!(k.len(), 6);
db.salsa_runtime().next_revision(); db.salsa_runtime().next_revision();
@ -80,7 +82,8 @@ fn two_rev_two_uses() {
// hence we keep 3 and 5 but remove the rest. // hence we keep 3 and 5 but remove the rest.
db.sweep_all(SweepStrategy::default()); db.sweep_all(SweepStrategy::default());
let mut k: Vec<_> = db.query(FibonacciQuery).keys(); assert_keys! {
k.sort(); db,
assert_eq!(k, vec![3, 5]); FibonacciQuery => (3, 5),
}
} }