mirror of
https://github.com/salsa-rs/salsa.git
synced 2024-12-24 21:03:59 +00:00
Merge pull request #122 from matklad/debug
allow to peek at values via debug query interface
This commit is contained in:
commit
3d1f9dac2d
8 changed files with 89 additions and 42 deletions
36
src/debug.rs
36
src/debug.rs
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
src/input.rs
11
src/input.rs
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue