use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MemoryEventType {
Allocate,
Deallocate,
Reallocate,
Move,
Borrow,
Return,
Metadata,
Clone,
}
impl fmt::Display for MemoryEventType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryEventType::Allocate => write!(f, "Allocate"),
MemoryEventType::Deallocate => write!(f, "Deallocate"),
MemoryEventType::Reallocate => write!(f, "Reallocate"),
MemoryEventType::Move => write!(f, "Move"),
MemoryEventType::Borrow => write!(f, "Borrow"),
MemoryEventType::Return => write!(f, "Return"),
MemoryEventType::Metadata => write!(f, "Metadata"),
MemoryEventType::Clone => write!(f, "Clone"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryEvent {
pub timestamp: u64,
pub event_type: MemoryEventType,
pub ptr: usize,
pub size: usize,
pub old_size: Option<usize>,
pub thread_id: u64,
pub var_name: Option<String>,
pub type_name: Option<String>,
pub call_stack_hash: Option<u64>,
pub thread_name: Option<String>,
pub source_file: Option<String>,
pub source_line: Option<u32>,
pub module_path: Option<String>,
pub clone_source_ptr: Option<usize>,
pub clone_target_ptr: Option<usize>,
pub stack_ptr: Option<usize>,
pub task_id: Option<u64>,
}
impl MemoryEvent {
pub fn allocate(ptr: usize, size: usize, thread_id: u64) -> Self {
crate::task_registry::global_registry().record_allocation(size);
Self {
timestamp: Self::now(),
event_type: MemoryEventType::Allocate,
ptr,
size,
old_size: None,
thread_id,
var_name: None,
type_name: None,
call_stack_hash: None,
thread_name: None,
source_file: None,
source_line: None,
module_path: None,
clone_source_ptr: None,
clone_target_ptr: None,
stack_ptr: None,
task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
}
}
pub fn deallocate(ptr: usize, size: usize, thread_id: u64) -> Self {
Self {
timestamp: Self::now(),
event_type: MemoryEventType::Deallocate,
ptr,
size,
old_size: None,
thread_id,
var_name: None,
type_name: None,
call_stack_hash: None,
thread_name: None,
source_file: None,
source_line: None,
module_path: None,
clone_source_ptr: None,
clone_target_ptr: None,
stack_ptr: None,
task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
}
}
pub fn reallocate(ptr: usize, old_size: usize, new_size: usize, thread_id: u64) -> Self {
Self {
timestamp: Self::now(),
event_type: MemoryEventType::Reallocate,
ptr,
size: new_size,
old_size: Some(old_size),
thread_id,
var_name: None,
type_name: None,
call_stack_hash: None,
thread_name: None,
source_file: None,
source_line: None,
module_path: None,
clone_source_ptr: None,
clone_target_ptr: None,
stack_ptr: None,
task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
}
}
pub fn metadata(var_name: String, type_name: String, thread_id: u64, size: usize) -> Self {
Self {
timestamp: Self::now(),
event_type: MemoryEventType::Metadata,
ptr: 0, size,
old_size: None,
thread_id,
var_name: Some(var_name),
type_name: Some(type_name),
call_stack_hash: None,
thread_name: None,
source_file: None,
source_line: None,
module_path: None,
clone_source_ptr: None,
clone_target_ptr: None,
stack_ptr: None,
task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
}
}
pub fn clone_event(
source_ptr: usize,
target_ptr: usize,
size: usize,
thread_id: u64,
var_name: Option<String>,
type_name: Option<String>,
) -> Self {
Self {
timestamp: Self::now(),
event_type: MemoryEventType::Clone,
ptr: target_ptr,
size,
old_size: None,
thread_id,
var_name,
type_name,
call_stack_hash: None,
thread_name: None,
source_file: None,
source_line: None,
module_path: None,
clone_source_ptr: Some(source_ptr),
clone_target_ptr: Some(target_ptr),
stack_ptr: None,
task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
}
}
pub fn borrow(ptr: usize, size: usize, thread_id: u64, is_mutable: bool) -> Self {
Self {
timestamp: Self::now(),
event_type: MemoryEventType::Borrow,
ptr,
size,
old_size: None,
thread_id,
var_name: None,
type_name: Some(if is_mutable {
"mutable borrow".to_string()
} else {
"immutable borrow".to_string()
}),
call_stack_hash: None,
thread_name: None,
source_file: None,
source_line: None,
module_path: None,
clone_source_ptr: None,
clone_target_ptr: None,
stack_ptr: None,
task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
}
}
pub fn now() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or_default()
}
pub fn with_var_name(mut self, name: String) -> Self {
self.var_name = Some(name);
self
}
pub fn with_type_name(mut self, name: String) -> Self {
self.type_name = Some(name);
self
}
pub fn with_source_file(mut self, file: String) -> Self {
self.source_file = Some(file);
self
}
pub fn with_source_line(mut self, line: u32) -> Self {
self.source_line = Some(line);
self
}
pub fn with_call_stack_hash(mut self, hash: u64) -> Self {
self.call_stack_hash = Some(hash);
self
}
pub fn with_thread_name(mut self, name: String) -> Self {
self.thread_name = Some(name);
self
}
pub fn is_allocation(&self) -> bool {
matches!(
self.event_type,
MemoryEventType::Allocate | MemoryEventType::Reallocate | MemoryEventType::Metadata
)
}
pub fn is_deallocation(&self) -> bool {
matches!(self.event_type, MemoryEventType::Deallocate)
}
pub fn is_move(&self) -> bool {
matches!(self.event_type, MemoryEventType::Move)
}
pub fn is_borrow(&self) -> bool {
matches!(self.event_type, MemoryEventType::Borrow)
}
pub fn is_return(&self) -> bool {
matches!(self.event_type, MemoryEventType::Return)
}
}