use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Event {
New {
timestamp: u64,
var_name: String,
var_id: String,
type_name: String,
},
Borrow {
timestamp: u64,
borrower_name: String,
borrower_id: String,
owner_id: String,
mutable: bool,
},
Move {
timestamp: u64,
from_id: String,
to_name: String,
to_id: String,
},
Drop { timestamp: u64, var_id: String },
RcNew {
timestamp: u64,
var_name: String,
var_id: String,
type_name: String,
strong_count: usize,
weak_count: usize,
},
RcClone {
timestamp: u64,
var_name: String,
var_id: String,
source_id: String,
strong_count: usize,
weak_count: usize,
},
ArcNew {
timestamp: u64,
var_name: String,
var_id: String,
type_name: String,
strong_count: usize,
weak_count: usize,
},
ArcClone {
timestamp: u64,
var_name: String,
var_id: String,
source_id: String,
strong_count: usize,
weak_count: usize,
},
RefCellNew {
timestamp: u64,
var_name: String,
var_id: String,
type_name: String,
},
RefCellBorrow {
timestamp: u64,
borrow_id: String,
refcell_id: String,
is_mutable: bool,
location: String,
},
RefCellDrop {
timestamp: u64,
borrow_id: String,
location: String,
},
CellNew {
timestamp: u64,
var_name: String,
var_id: String,
type_name: String,
},
CellGet {
timestamp: u64,
cell_id: String,
location: String,
},
CellSet {
timestamp: u64,
cell_id: String,
location: String,
},
StaticInit {
timestamp: u64,
var_name: String,
var_id: String,
type_name: String,
is_mutable: bool,
},
StaticAccess {
timestamp: u64,
var_id: String,
var_name: String,
is_write: bool,
location: String,
},
ConstEval {
timestamp: u64,
const_name: String,
const_id: String,
type_name: String,
location: String,
},
RawPtrCreated {
timestamp: u64,
var_name: String,
var_id: String,
ptr_type: String,
address: usize,
location: String,
},
RawPtrDeref {
timestamp: u64,
ptr_id: String,
location: String,
is_write: bool,
},
UnsafeBlockEnter {
timestamp: u64,
block_id: String,
location: String,
},
UnsafeBlockExit {
timestamp: u64,
block_id: String,
location: String,
},
UnsafeFnCall {
timestamp: u64,
fn_name: String,
location: String,
},
FfiCall {
timestamp: u64,
fn_name: String,
location: String,
},
Transmute {
timestamp: u64,
from_type: String,
to_type: String,
location: String,
},
UnionFieldAccess {
timestamp: u64,
union_name: String,
field_name: String,
location: String,
},
AsyncBlockEnter {
timestamp: u64,
block_id: String,
location: String,
},
AsyncBlockExit {
timestamp: u64,
block_id: String,
location: String,
},
AwaitStart {
timestamp: u64,
await_id: String,
future_name: String,
location: String,
},
AwaitEnd {
timestamp: u64,
await_id: String,
location: String,
},
LoopEnter {
timestamp: u64,
loop_id: String,
loop_type: String,
location: String,
},
LoopIteration {
timestamp: u64,
loop_id: String,
iteration: usize,
location: String,
},
LoopExit {
timestamp: u64,
loop_id: String,
location: String,
},
MatchEnter {
timestamp: u64,
match_id: String,
location: String,
},
MatchArm {
timestamp: u64,
match_id: String,
arm_index: usize,
pattern: String,
location: String,
},
MatchExit {
timestamp: u64,
match_id: String,
location: String,
},
Branch {
timestamp: u64,
branch_id: String,
branch_type: String,
location: String,
},
Return {
timestamp: u64,
return_id: String,
has_value: bool,
location: String,
},
Try {
timestamp: u64,
try_id: String,
location: String,
},
IndexAccess {
timestamp: u64,
access_id: String,
container: String,
location: String,
},
FieldAccess {
timestamp: u64,
access_id: String,
base: String,
field: String,
location: String,
},
Call {
timestamp: u64,
call_id: String,
fn_name: String,
location: String,
},
Lock {
timestamp: u64,
lock_id: String,
lock_type: String,
var_name: String,
location: String,
},
Unwrap {
timestamp: u64,
unwrap_id: String,
method: String,
var_name: String,
location: String,
},
Clone {
timestamp: u64,
clone_id: String,
var_name: String,
location: String,
},
Deref {
timestamp: u64,
deref_id: String,
var_name: String,
location: String,
},
Break {
timestamp: u64,
break_id: String,
loop_label: Option<String>,
location: String,
},
Continue {
timestamp: u64,
continue_id: String,
loop_label: Option<String>,
location: String,
},
ClosureCreate {
timestamp: u64,
closure_id: String,
capture_mode: String,
location: String,
},
StructCreate {
timestamp: u64,
struct_id: String,
type_name: String,
location: String,
},
TupleCreate {
timestamp: u64,
tuple_id: String,
len: usize,
location: String,
},
LetElse {
timestamp: u64,
let_id: String,
pattern: String,
location: String,
},
Range {
timestamp: u64,
range_id: String,
range_type: String,
location: String,
},
BinaryOp {
timestamp: u64,
op_id: String,
operator: String,
location: String,
},
ArrayCreate {
timestamp: u64,
array_id: String,
len: usize,
location: String,
},
TypeCast {
timestamp: u64,
cast_id: String,
to_type: String,
location: String,
},
RegionEnter {
timestamp: u64,
region_id: String,
name: String,
location: String,
},
RegionExit {
timestamp: u64,
region_id: String,
location: String,
},
FnEnter {
timestamp: u64,
fn_id: String,
fn_name: String,
location: String,
},
FnExit {
timestamp: u64,
fn_id: String,
fn_name: String,
location: String,
},
ClosureCapture {
timestamp: u64,
closure_id: String,
var_name: String,
capture_mode: String,
location: String,
},
WeakNew {
timestamp: u64,
var_name: String,
var_id: String,
source_id: String,
weak_count: usize,
location: String,
},
WeakClone {
timestamp: u64,
var_name: String,
var_id: String,
source_id: String,
weak_count: usize,
location: String,
},
WeakUpgrade {
timestamp: u64,
weak_id: String,
success: bool,
location: String,
},
BoxNew {
timestamp: u64,
var_name: String,
var_id: String,
type_name: String,
location: String,
},
BoxIntoRaw {
timestamp: u64,
box_id: String,
location: String,
},
BoxFromRaw {
timestamp: u64,
var_name: String,
var_id: String,
location: String,
},
LockGuardAcquire {
timestamp: u64,
guard_id: String,
lock_id: String,
lock_type: String,
location: String,
},
LockGuardDrop {
timestamp: u64,
guard_id: String,
location: String,
},
PinNew {
timestamp: u64,
var_name: String,
var_id: String,
location: String,
},
PinIntoInner {
timestamp: u64,
pin_id: String,
location: String,
},
CowBorrowed {
timestamp: u64,
var_name: String,
var_id: String,
location: String,
},
CowOwned {
timestamp: u64,
var_name: String,
var_id: String,
location: String,
},
CowToMut {
timestamp: u64,
cow_id: String,
cloned: bool,
location: String,
},
ThreadSpawn {
timestamp: u64,
thread_id: String,
location: String,
},
ThreadJoin {
timestamp: u64,
thread_id: String,
location: String,
},
ChannelSenderNew {
timestamp: u64,
sender_id: String,
channel_id: String,
location: String,
},
ChannelReceiverNew {
timestamp: u64,
receiver_id: String,
channel_id: String,
location: String,
},
ChannelSend {
timestamp: u64,
sender_id: String,
location: String,
},
ChannelRecv {
timestamp: u64,
receiver_id: String,
success: bool,
location: String,
},
OnceCellNew {
timestamp: u64,
var_name: String,
var_id: String,
cell_type: String, location: String,
},
OnceCellSet {
timestamp: u64,
cell_id: String,
success: bool,
location: String,
},
OnceCellGet {
timestamp: u64,
cell_id: String,
was_initialized: bool,
location: String,
},
OnceCellGetOrInit {
timestamp: u64,
cell_id: String,
was_initialized: bool,
location: String,
},
MaybeUninitNew {
timestamp: u64,
var_name: String,
var_id: String,
initialized: bool,
location: String,
},
MaybeUninitWrite {
timestamp: u64,
var_id: String,
location: String,
},
MaybeUninitAssumeInit {
timestamp: u64,
var_id: String,
location: String,
},
MaybeUninitAssumeInitRead {
timestamp: u64,
var_id: String,
location: String,
},
MaybeUninitAssumeInitDrop {
timestamp: u64,
var_id: String,
location: String,
},
}
impl Event {
pub fn timestamp(&self) -> u64 {
match self {
Event::New { timestamp, .. }
| Event::Borrow { timestamp, .. }
| Event::Move { timestamp, .. }
| Event::Drop { timestamp, .. }
| Event::RcNew { timestamp, .. }
| Event::RcClone { timestamp, .. }
| Event::ArcNew { timestamp, .. }
| Event::ArcClone { timestamp, .. }
| Event::RefCellNew { timestamp, .. }
| Event::RefCellBorrow { timestamp, .. }
| Event::RefCellDrop { timestamp, .. }
| Event::CellNew { timestamp, .. }
| Event::CellGet { timestamp, .. }
| Event::CellSet { timestamp, .. }
| Event::StaticInit { timestamp, .. }
| Event::StaticAccess { timestamp, .. }
| Event::ConstEval { timestamp, .. }
| Event::RawPtrCreated { timestamp, .. }
| Event::RawPtrDeref { timestamp, .. }
| Event::UnsafeBlockEnter { timestamp, .. }
| Event::UnsafeBlockExit { timestamp, .. }
| Event::UnsafeFnCall { timestamp, .. }
| Event::FfiCall { timestamp, .. }
| Event::Transmute { timestamp, .. }
| Event::UnionFieldAccess { timestamp, .. }
| Event::AsyncBlockEnter { timestamp, .. }
| Event::AsyncBlockExit { timestamp, .. }
| Event::AwaitStart { timestamp, .. }
| Event::AwaitEnd { timestamp, .. }
| Event::LoopEnter { timestamp, .. }
| Event::LoopIteration { timestamp, .. }
| Event::LoopExit { timestamp, .. }
| Event::MatchEnter { timestamp, .. }
| Event::MatchArm { timestamp, .. }
| Event::MatchExit { timestamp, .. }
| Event::Branch { timestamp, .. }
| Event::Return { timestamp, .. }
| Event::Try { timestamp, .. }
| Event::IndexAccess { timestamp, .. }
| Event::FieldAccess { timestamp, .. }
| Event::Call { timestamp, .. }
| Event::Lock { timestamp, .. }
| Event::Unwrap { timestamp, .. }
| Event::Clone { timestamp, .. }
| Event::Deref { timestamp, .. }
| Event::Break { timestamp, .. }
| Event::Continue { timestamp, .. }
| Event::ClosureCreate { timestamp, .. }
| Event::StructCreate { timestamp, .. }
| Event::TupleCreate { timestamp, .. }
| Event::LetElse { timestamp, .. }
| Event::Range { timestamp, .. }
| Event::BinaryOp { timestamp, .. }
| Event::ArrayCreate { timestamp, .. }
| Event::TypeCast { timestamp, .. }
| Event::RegionEnter { timestamp, .. }
| Event::RegionExit { timestamp, .. }
| Event::FnEnter { timestamp, .. }
| Event::FnExit { timestamp, .. }
| Event::ClosureCapture { timestamp, .. }
| Event::WeakNew { timestamp, .. }
| Event::WeakClone { timestamp, .. }
| Event::WeakUpgrade { timestamp, .. }
| Event::BoxNew { timestamp, .. }
| Event::BoxIntoRaw { timestamp, .. }
| Event::BoxFromRaw { timestamp, .. }
| Event::LockGuardAcquire { timestamp, .. }
| Event::LockGuardDrop { timestamp, .. }
| Event::PinNew { timestamp, .. }
| Event::PinIntoInner { timestamp, .. }
| Event::CowBorrowed { timestamp, .. }
| Event::CowOwned { timestamp, .. }
| Event::CowToMut { timestamp, .. }
| Event::ThreadSpawn { timestamp, .. }
| Event::ThreadJoin { timestamp, .. }
| Event::ChannelSenderNew { timestamp, .. }
| Event::ChannelReceiverNew { timestamp, .. }
| Event::ChannelSend { timestamp, .. }
| Event::ChannelRecv { timestamp, .. }
| Event::OnceCellNew { timestamp, .. }
| Event::OnceCellSet { timestamp, .. }
| Event::OnceCellGet { timestamp, .. }
| Event::OnceCellGetOrInit { timestamp, .. }
| Event::MaybeUninitNew { timestamp, .. }
| Event::MaybeUninitWrite { timestamp, .. }
| Event::MaybeUninitAssumeInit { timestamp, .. }
| Event::MaybeUninitAssumeInitRead { timestamp, .. }
| Event::MaybeUninitAssumeInitDrop { timestamp, .. } => *timestamp,
}
}
pub fn var_name(&self) -> Option<&str> {
match self {
Event::New { var_name, .. }
| Event::RcNew { var_name, .. }
| Event::RcClone { var_name, .. }
| Event::ArcNew { var_name, .. }
| Event::ArcClone { var_name, .. }
| Event::RefCellNew { var_name, .. }
| Event::CellNew { var_name, .. }
| Event::StaticInit { var_name, .. }
| Event::StaticAccess { var_name, .. }
| Event::RawPtrCreated { var_name, .. }
| Event::ConstEval {
const_name: var_name,
..
} => Some(var_name),
Event::Borrow { borrower_name, .. } => Some(borrower_name),
Event::Move { to_name, .. } => Some(to_name),
Event::Drop { var_id, .. } => Some(var_id),
Event::RefCellBorrow { .. }
| Event::RefCellDrop { .. }
| Event::CellGet { .. }
| Event::CellSet { .. }
| Event::RawPtrDeref { .. }
| Event::UnsafeBlockEnter { .. }
| Event::UnsafeBlockExit { .. }
| Event::UnsafeFnCall { .. }
| Event::FfiCall { .. }
| Event::Transmute { .. }
| Event::UnionFieldAccess { .. }
| Event::AsyncBlockEnter { .. }
| Event::AsyncBlockExit { .. }
| Event::AwaitStart { .. }
| Event::AwaitEnd { .. }
| Event::LoopEnter { .. }
| Event::LoopIteration { .. }
| Event::LoopExit { .. }
| Event::MatchEnter { .. }
| Event::MatchArm { .. }
| Event::MatchExit { .. }
| Event::Branch { .. }
| Event::Return { .. }
| Event::Try { .. }
| Event::IndexAccess { .. }
| Event::FieldAccess { .. }
| Event::Call { .. }
| Event::Lock { .. }
| Event::Unwrap { .. }
| Event::Clone { .. }
| Event::Deref { .. }
| Event::Break { .. }
| Event::Continue { .. }
| Event::ClosureCreate { .. }
| Event::StructCreate { .. }
| Event::TupleCreate { .. }
| Event::LetElse { .. }
| Event::Range { .. }
| Event::BinaryOp { .. }
| Event::ArrayCreate { .. }
| Event::TypeCast { .. }
| Event::RegionEnter { .. }
| Event::RegionExit { .. }
| Event::FnEnter { .. }
| Event::FnExit { .. }
| Event::ClosureCapture { .. }
| Event::WeakUpgrade { .. }
| Event::BoxIntoRaw { .. }
| Event::LockGuardAcquire { .. }
| Event::LockGuardDrop { .. }
| Event::PinIntoInner { .. }
| Event::CowToMut { .. }
| Event::ThreadSpawn { .. }
| Event::ThreadJoin { .. }
| Event::ChannelSend { .. }
| Event::ChannelRecv { .. }
| Event::OnceCellSet { .. }
| Event::OnceCellGet { .. }
| Event::OnceCellGetOrInit { .. }
| Event::MaybeUninitWrite { .. }
| Event::MaybeUninitAssumeInit { .. }
| Event::MaybeUninitAssumeInitRead { .. }
| Event::MaybeUninitAssumeInitDrop { .. } => None,
Event::WeakNew { var_name, .. }
| Event::WeakClone { var_name, .. }
| Event::BoxNew { var_name, .. }
| Event::BoxFromRaw { var_name, .. }
| Event::PinNew { var_name, .. }
| Event::CowBorrowed { var_name, .. }
| Event::CowOwned { var_name, .. }
| Event::OnceCellNew { var_name, .. }
| Event::MaybeUninitNew { var_name, .. } => Some(var_name),
Event::ChannelSenderNew { sender_id, .. } => Some(sender_id),
Event::ChannelReceiverNew { receiver_id, .. } => Some(receiver_id),
}
}
pub fn is_new(&self) -> bool {
matches!(self, Event::New { .. })
}
pub fn is_borrow(&self) -> bool {
matches!(self, Event::Borrow { .. })
}
pub fn is_move(&self) -> bool {
matches!(self, Event::Move { .. })
}
pub fn is_drop(&self) -> bool {
matches!(self, Event::Drop { .. })
}
pub fn is_rc(&self) -> bool {
matches!(self, Event::RcNew { .. } | Event::RcClone { .. })
}
pub fn is_arc(&self) -> bool {
matches!(self, Event::ArcNew { .. } | Event::ArcClone { .. })
}
pub fn is_refcounted(&self) -> bool {
self.is_rc() || self.is_arc()
}
pub fn is_refcell(&self) -> bool {
matches!(
self,
Event::RefCellNew { .. } | Event::RefCellBorrow { .. } | Event::RefCellDrop { .. }
)
}
pub fn is_cell(&self) -> bool {
matches!(
self,
Event::CellNew { .. } | Event::CellGet { .. } | Event::CellSet { .. }
)
}
pub fn is_interior_mutability(&self) -> bool {
self.is_refcell() || self.is_cell()
}
pub fn is_static(&self) -> bool {
matches!(self, Event::StaticInit { .. } | Event::StaticAccess { .. })
}
pub fn is_const(&self) -> bool {
matches!(self, Event::ConstEval { .. })
}
pub fn is_global(&self) -> bool {
self.is_static() || self.is_const()
}
pub fn is_unsafe(&self) -> bool {
matches!(
self,
Event::RawPtrCreated { .. }
| Event::RawPtrDeref { .. }
| Event::UnsafeBlockEnter { .. }
| Event::UnsafeBlockExit { .. }
| Event::UnsafeFnCall { .. }
| Event::FfiCall { .. }
| Event::Transmute { .. }
| Event::UnionFieldAccess { .. }
)
}
pub fn is_raw_ptr(&self) -> bool {
matches!(
self,
Event::RawPtrCreated { .. } | Event::RawPtrDeref { .. }
)
}
pub fn is_ffi(&self) -> bool {
matches!(self, Event::FfiCall { .. })
}
pub fn strong_count(&self) -> Option<usize> {
match self {
Event::RcNew { strong_count, .. }
| Event::RcClone { strong_count, .. }
| Event::ArcNew { strong_count, .. }
| Event::ArcClone { strong_count, .. } => Some(*strong_count),
_ => None,
}
}
pub fn weak_count(&self) -> Option<usize> {
match self {
Event::RcNew { weak_count, .. }
| Event::RcClone { weak_count, .. }
| Event::ArcNew { weak_count, .. }
| Event::ArcClone { weak_count, .. }
| Event::WeakNew { weak_count, .. }
| Event::WeakClone { weak_count, .. } => Some(*weak_count),
_ => None,
}
}
pub fn is_weak(&self) -> bool {
matches!(
self,
Event::WeakNew { .. } | Event::WeakClone { .. } | Event::WeakUpgrade { .. }
)
}
pub fn is_box(&self) -> bool {
matches!(
self,
Event::BoxNew { .. } | Event::BoxIntoRaw { .. } | Event::BoxFromRaw { .. }
)
}
pub fn is_lock_guard(&self) -> bool {
matches!(
self,
Event::LockGuardAcquire { .. } | Event::LockGuardDrop { .. }
)
}
pub fn is_pin(&self) -> bool {
matches!(self, Event::PinNew { .. } | Event::PinIntoInner { .. })
}
pub fn is_cow(&self) -> bool {
matches!(
self,
Event::CowBorrowed { .. } | Event::CowOwned { .. } | Event::CowToMut { .. }
)
}
pub fn is_thread(&self) -> bool {
matches!(self, Event::ThreadSpawn { .. } | Event::ThreadJoin { .. })
}
pub fn is_channel(&self) -> bool {
matches!(
self,
Event::ChannelSenderNew { .. }
| Event::ChannelReceiverNew { .. }
| Event::ChannelSend { .. }
| Event::ChannelRecv { .. }
)
}
pub fn is_once_cell(&self) -> bool {
matches!(
self,
Event::OnceCellNew { .. }
| Event::OnceCellSet { .. }
| Event::OnceCellGet { .. }
| Event::OnceCellGetOrInit { .. }
)
}
pub fn is_maybe_uninit(&self) -> bool {
matches!(
self,
Event::MaybeUninitNew { .. }
| Event::MaybeUninitWrite { .. }
| Event::MaybeUninitAssumeInit { .. }
| Event::MaybeUninitAssumeInitRead { .. }
| Event::MaybeUninitAssumeInitDrop { .. }
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_event_new() {
let event = Event::New {
timestamp: 1,
var_name: "x".to_string(),
var_id: "x_0".to_string(),
type_name: "i32".to_string(),
};
assert_eq!(event.timestamp(), 1);
assert_eq!(event.var_name(), Some("x"));
assert!(event.is_new());
assert!(!event.is_borrow());
assert!(!event.is_move());
assert!(!event.is_drop());
}
#[test]
fn test_event_borrow() {
let event = Event::Borrow {
timestamp: 2,
borrower_name: "r".to_string(),
borrower_id: "r_1".to_string(),
owner_id: "x_0".to_string(),
mutable: false,
};
assert_eq!(event.timestamp(), 2);
assert_eq!(event.var_name(), Some("r"));
assert!(event.is_borrow());
assert!(!event.is_new());
}
#[test]
fn test_event_move() {
let event = Event::Move {
timestamp: 3,
from_id: "x_0".to_string(),
to_name: "y".to_string(),
to_id: "y_1".to_string(),
};
assert_eq!(event.timestamp(), 3);
assert_eq!(event.var_name(), Some("y"));
assert!(event.is_move());
}
#[test]
fn test_event_drop() {
let event = Event::Drop {
timestamp: 4,
var_id: "x_0".to_string(),
};
assert_eq!(event.timestamp(), 4);
assert!(event.is_drop());
}
#[test]
fn test_event_serialization() {
let event = Event::New {
timestamp: 1,
var_name: "x".to_string(),
var_id: "x_0".to_string(),
type_name: "i32".to_string(),
};
let json = serde_json::to_string(&event).unwrap();
let deserialized: Event = serde_json::from_str(&json).unwrap();
assert_eq!(event, deserialized);
}
#[test]
fn test_borrow_mutable_flag() {
let immut = Event::Borrow {
timestamp: 1,
borrower_name: "r".to_string(),
borrower_id: "r_0".to_string(),
owner_id: "x_0".to_string(),
mutable: false,
};
let mut_borrow = Event::Borrow {
timestamp: 2,
borrower_name: "r".to_string(),
borrower_id: "r_1".to_string(),
owner_id: "x_0".to_string(),
mutable: true,
};
assert_ne!(immut, mut_borrow);
}
}