internal: track missing .check_cancelled

This commit is contained in:
Aleksey Kladov 2021-03-29 21:08:07 +03:00
parent 2381a54c2f
commit 483a5bc9c8
3 changed files with 81 additions and 11 deletions

View file

@ -59,6 +59,8 @@ pub trait CheckCanceled {
Self: Sized + panic::RefUnwindSafe, Self: Sized + panic::RefUnwindSafe,
F: FnOnce(&Self) -> T + panic::UnwindSafe, F: FnOnce(&Self) -> T + panic::UnwindSafe,
{ {
// Uncomment to debug missing cancellations.
// let _span = profile::heartbeat_span();
panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() { panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() {
Ok(canceled) => *canceled, Ok(canceled) => *canceled,
Err(payload) => panic::resume_unwind(payload), Err(payload) => panic::resume_unwind(payload),
@ -68,6 +70,7 @@ pub trait CheckCanceled {
impl<T: salsa::Database> CheckCanceled for T { impl<T: salsa::Database> CheckCanceled for T {
fn check_canceled(&self) { fn check_canceled(&self) {
// profile::heartbeat();
if self.salsa_runtime().is_current_revision_canceled() { if self.salsa_runtime().is_current_revision_canceled() {
Canceled::throw() Canceled::throw()
} }

View file

@ -69,6 +69,20 @@ pub fn span(label: Label) -> ProfileSpan {
} }
} }
#[inline]
pub fn heartbeat_span() -> HeartbeatSpan {
let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
HeartbeatSpan::new(enabled)
}
#[inline]
pub fn heartbeat() {
let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
if enabled {
with_profile_stack(|it| it.heartbeat(1));
}
}
pub struct ProfileSpan(Option<ProfilerImpl>); pub struct ProfileSpan(Option<ProfilerImpl>);
struct ProfilerImpl { struct ProfilerImpl {
@ -92,6 +106,28 @@ impl Drop for ProfilerImpl {
} }
} }
pub struct HeartbeatSpan {
enabled: bool,
}
impl HeartbeatSpan {
#[inline]
pub fn new(enabled: bool) -> Self {
if enabled {
with_profile_stack(|it| it.heartbeats(true))
}
Self { enabled }
}
}
impl Drop for HeartbeatSpan {
fn drop(&mut self) {
if self.enabled {
with_profile_stack(|it| it.heartbeats(false))
}
}
}
static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false); static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default); static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);
@ -105,6 +141,7 @@ struct Filter {
depth: usize, depth: usize,
allowed: HashSet<String>, allowed: HashSet<String>,
longer_than: Duration, longer_than: Duration,
heartbeat_longer_than: Duration,
version: usize, version: usize,
} }
@ -121,6 +158,7 @@ impl Filter {
} else { } else {
Duration::new(0, 0) Duration::new(0, 0)
}; };
let heartbeat_longer_than = longer_than;
let depth = if let Some(idx) = spec.rfind('@') { let depth = if let Some(idx) = spec.rfind('@') {
let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth"); let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
@ -131,7 +169,7 @@ impl Filter {
}; };
let allowed = let allowed =
if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() }; if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
Filter { depth, allowed, longer_than, version: 0 } Filter { depth, allowed, longer_than, heartbeat_longer_than, version: 0 }
} }
fn install(mut self) { fn install(mut self) {
@ -143,9 +181,15 @@ impl Filter {
} }
struct ProfileStack { struct ProfileStack {
starts: Vec<Instant>, frames: Vec<Frame>,
filter: Filter, filter: Filter,
messages: Tree<Message>, messages: Tree<Message>,
heartbeats: bool,
}
struct Frame {
t: Instant,
heartbeats: u32,
} }
#[derive(Default)] #[derive(Default)]
@ -157,35 +201,49 @@ struct Message {
impl ProfileStack { impl ProfileStack {
fn new() -> ProfileStack { fn new() -> ProfileStack {
ProfileStack { starts: Vec::new(), messages: Tree::default(), filter: Default::default() } ProfileStack {
frames: Vec::new(),
messages: Tree::default(),
filter: Default::default(),
heartbeats: false,
}
} }
fn push(&mut self, label: Label) -> bool { fn push(&mut self, label: Label) -> bool {
if self.starts.is_empty() { if self.frames.is_empty() {
if let Ok(f) = FILTER.try_read() { if let Ok(f) = FILTER.try_read() {
if f.version > self.filter.version { if f.version > self.filter.version {
self.filter = f.clone(); self.filter = f.clone();
} }
}; };
} }
if self.starts.len() > self.filter.depth { if self.frames.len() > self.filter.depth {
return false; return false;
} }
let allowed = &self.filter.allowed; let allowed = &self.filter.allowed;
if self.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) { if self.frames.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
return false; return false;
} }
self.starts.push(Instant::now()); self.frames.push(Frame { t: Instant::now(), heartbeats: 0 });
self.messages.start(); self.messages.start();
true true
} }
fn pop(&mut self, label: Label, detail: Option<String>) { fn pop(&mut self, label: Label, detail: Option<String>) {
let start = self.starts.pop().unwrap(); let frame = self.frames.pop().unwrap();
let duration = start.elapsed(); let duration = frame.t.elapsed();
if self.heartbeats {
self.heartbeat(frame.heartbeats);
let avg_span = duration / (frame.heartbeats + 1);
if avg_span > self.filter.heartbeat_longer_than {
eprintln!("Too few heartbeats {} ({}/{:?})?", label, frame.heartbeats, duration)
}
}
self.messages.finish(Message { duration, label, detail }); self.messages.finish(Message { duration, label, detail });
if self.starts.is_empty() { if self.frames.is_empty() {
let longer_than = self.filter.longer_than; let longer_than = self.filter.longer_than;
// Convert to millis for comparison to avoid problems with rounding // Convert to millis for comparison to avoid problems with rounding
// (otherwise we could print `0ms` despite user's `>0` filter when // (otherwise we could print `0ms` despite user's `>0` filter when
@ -198,6 +256,15 @@ impl ProfileStack {
self.messages.clear(); self.messages.clear();
} }
} }
fn heartbeats(&mut self, yes: bool) {
self.heartbeats = yes;
}
fn heartbeat(&mut self, n: u32) {
if let Some(frame) = self.frames.last_mut() {
frame.heartbeats += n;
}
}
} }
fn print( fn print(

View file

@ -10,7 +10,7 @@ mod tree;
use std::cell::RefCell; use std::cell::RefCell;
pub use crate::{ pub use crate::{
hprof::{init, init_from, span}, hprof::{heartbeat, heartbeat_span, init, init_from, span},
memory_usage::{Bytes, MemoryUsage}, memory_usage::{Bytes, MemoryUsage},
stop_watch::{StopWatch, StopWatchSpan}, stop_watch::{StopWatch, StopWatchSpan},
}; };