Skip to content

Commit

Permalink
basic backtrace tracking (#110)
Browse files Browse the repository at this point in the history
An initial pass at tracking backtraces during execution. Currently, only
`Arc::new` is tracked, but now that infrastructure is added, tracking
additional events should be easier.

Capturing a backtrace is a an expensive event. The failure case should
be isolated before capturing backtraces.
  • Loading branch information
carllerche authored Mar 3, 2020
1 parent a89fef9 commit 977067f
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ serde = { version = "1.0.92", features = ["derive"], optional = true }
serde_json = { version = "1.0.33", optional = true }

futures-util = { version = "0.3.0", optional = true }
backtrace = { version = "0.3.44", optional = true }
13 changes: 13 additions & 0 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ pub struct Builder {
/// Defaults to `LOOM_CHECKPOINT_INTERVAL` environment variable.
pub checkpoint_interval: usize,

/// When `true`, backtraces are captured on each loom operation.
///
/// Note that is is **very** expensive. It is recommended to first isolate a
/// failing iteration using `LOOM_CHECKPOINT_FILE`, then enable backtrace
/// collection.
///
/// Defaults to `LOOM_BACKTRACE` environment variable.
pub backtrace: bool,

/// Log execution output to stdout.
///
/// Defaults to existance of `LOOM_LOG` environment variable.
Expand Down Expand Up @@ -75,6 +84,8 @@ impl Builder {
})
.unwrap_or(DEFAULT_MAX_BRANCHES);

let backtrace = env::var("LOOM_BACKTRACE").is_ok();

let log = env::var("LOOM_LOG").is_ok();

let max_duration = env::var("LOOM_MAX_DURATION")
Expand Down Expand Up @@ -119,6 +130,7 @@ impl Builder {
preemption_bound,
checkpoint_file,
checkpoint_interval,
backtrace,
log,
_p: (),
}
Expand Down Expand Up @@ -146,6 +158,7 @@ impl Builder {
}

execution.log = self.log;
execution.backtrace = self.backtrace;

let f = Arc::new(f);

Expand Down
18 changes: 15 additions & 3 deletions src/rt/arc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![allow(warnings)]
use crate::rt::object::Object;
use crate::rt::{self, Access, Synchronize, VersionVec};
use crate::rt::{self, Access, Backtrace, Synchronize, VersionVec};

use std::sync::atomic::Ordering::{Acquire, Release};

Expand All @@ -14,6 +13,9 @@ pub(super) struct State {
/// Reference count
ref_cnt: usize,

/// Backtrace where the arc was allocated
allocated: Option<Backtrace>,

/// Causality transfers between threads
///
/// Only updated on on ref dec and acquired before drop
Expand Down Expand Up @@ -47,6 +49,7 @@ impl Arc {
rt::execution(|execution| {
let obj = execution.objects.insert_arc(State {
ref_cnt: 1,
allocated: execution.backtrace(),
synchronize: Synchronize::new(execution.max_threads),
last_ref_inc: None,
last_ref_dec: None,
Expand Down Expand Up @@ -118,7 +121,16 @@ impl Arc {

impl State {
pub(super) fn check_for_leaks(&self) {
assert_eq!(0, self.ref_cnt, "Arc leaked");
if self.ref_cnt != 0 {
if let Some(backtrace) = &self.allocated {
panic!(
"Arc leaked.\n------------\nAllocated:\n\n{}\n------------\n",
backtrace
);
} else {
panic!("Arc leaked.");
}
}
}

pub(super) fn last_dependent_access(&self, action: Action) -> Option<&Access> {
Expand Down
42 changes: 42 additions & 0 deletions src/rt/backtrace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
pub(crate) use cfg::Backtrace;

#[cfg(feature = "backtrace")]
mod cfg {
use std::fmt;

#[derive(Debug)]
pub(crate) struct Backtrace(backtrace::Backtrace);

impl Backtrace {
pub(crate) fn capture() -> Backtrace {
Backtrace(backtrace::Backtrace::new())
}
}

impl fmt::Display for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Add some sort of filter to get rid of loom internals
write!(fmt, "{:?}", self.0)
}
}
}

#[cfg(not(feature = "backtrace"))]
mod cfg {
use std::fmt;

#[derive(Debug)]
pub(crate) struct Backtrace;

impl Backtrace {
pub(crate) fn capture() -> Backtrace {
panic!("enable `backtrace` feature flag");
}
}

impl fmt::Display for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "[enable `backtrace` feature for backtrace capture]")
}
}
}
18 changes: 17 additions & 1 deletion src/rt/execution.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::rt::alloc::Allocation;
use crate::rt::{object, thread, Path};
use crate::rt::{object, thread, Backtrace, Path};

use std::collections::HashMap;
use std::fmt;
Expand All @@ -24,6 +24,9 @@ pub(crate) struct Execution {

pub(super) max_history: usize,

/// Capture backtraces for significant events
pub(crate) backtrace: bool,

/// Log execution output to STDOUT
pub(crate) log: bool,
}
Expand Down Expand Up @@ -52,6 +55,7 @@ impl Execution {
raw_allocations: HashMap::new(),
max_threads,
max_history: 7,
backtrace: false,
log: false,
}
}
Expand Down Expand Up @@ -79,6 +83,7 @@ impl Execution {
let id = Id::new();
let max_threads = self.max_threads;
let max_history = self.max_history;
let backtrace = self.backtrace;
let log = self.log;
let mut path = self.path;
let mut objects = self.objects;
Expand All @@ -103,6 +108,7 @@ impl Execution {
raw_allocations,
max_threads,
max_history,
backtrace,
log,
})
}
Expand Down Expand Up @@ -238,6 +244,16 @@ impl Execution {
pub(crate) fn unset_critical(&mut self) {
self.threads.active_mut().critical = false;
}

// Never inline so we can ensure loom shows up at the top of the backtrace.
#[inline(never)]
pub(crate) fn backtrace(&self) -> Option<Backtrace> {
if self.backtrace {
Some(Backtrace::capture())
} else {
None
}
}
}

impl fmt::Debug for Execution {
Expand Down
3 changes: 3 additions & 0 deletions src/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub(crate) use self::arc::Arc;
mod atomic;
pub(crate) use self::atomic::{fence, Atomic};

mod backtrace;
pub(crate) use self::backtrace::Backtrace;

mod condvar;
pub(crate) use self::condvar::Condvar;

Expand Down

0 comments on commit 977067f

Please sign in to comment.