use std::collections::HashMap;
use std::fmt;
use std::thread::ThreadId;
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryEventType {
Allocation,
Deallocation,
Resize,
Access,
Transfer,
}
impl fmt::Display for MemoryEventType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryEventType::Allocation => write!(f, "Allocation"),
MemoryEventType::Deallocation => write!(f, "Deallocation"),
MemoryEventType::Resize => write!(f, "Resize"),
MemoryEventType::Access => write!(f, "Access"),
MemoryEventType::Transfer => write!(f, "Transfer"),
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryEvent {
pub event_type: MemoryEventType,
pub component: String,
pub context: Option<String>,
pub size: usize,
pub address: usize,
pub thread_id: ThreadId,
pub timestamp: Instant,
pub call_stack: Option<Vec<String>>,
pub metadata: HashMap<String, String>,
}
impl MemoryEvent {
pub fn new(
event_type: MemoryEventType,
component: impl Into<String>,
size: usize,
address: usize,
) -> Self {
Self {
event_type,
component: component.into(),
context: None,
size,
address,
thread_id: std::thread::current().id(),
timestamp: Instant::now(),
call_stack: None,
metadata: HashMap::new(),
}
}
pub fn with_context(mut self, context: impl Into<String>) -> Self {
self.context = Some(context.into());
self
}
pub fn with_call_stack(mut self) -> Self {
if cfg!(feature = "memory_call_stack") {
self.call_stack = Some(capture_call_stack(3)); }
self
}
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
}
fn capture_call_stack(skipframes: usize) -> Vec<String> {
vec![format!("frame_{}", skipframes)]
}
#[cfg(feature = "memory_call_stack")]
#[allow(dead_code)]
fn capture_stack_frames(maxframes: usize) -> Vec<String> {
vec!["<callstack not available>".to_string()]
}
#[cfg(not(feature = "memory_call_stack"))]
#[allow(dead_code)]
fn capture_stack_frames(maxframes: usize) -> Vec<String> {
Vec::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_event_creation() {
let event = MemoryEvent::new(
MemoryEventType::Allocation,
"TestComponent",
1024,
0xdeadbeef,
);
assert_eq!(event.event_type, MemoryEventType::Allocation);
assert_eq!(event.component, "TestComponent");
assert_eq!(event.size, 1024);
assert_eq!(event.address, 0xdeadbeef);
assert_eq!(event.context, None);
assert!(event.call_stack.is_none());
assert!(event.metadata.is_empty());
}
#[test]
fn test_memory_event_with_context() {
let event = MemoryEvent::new(
MemoryEventType::Allocation,
"TestComponent",
1024,
0xdeadbeef,
)
.with_context("TestContext");
assert_eq!(event.context, Some("TestContext".to_string()));
}
#[test]
fn test_memory_event_with_metadata() {
let event = MemoryEvent::new(
MemoryEventType::Allocation,
"TestComponent",
1024,
0xdeadbeef,
)
.with_metadata("key1", "value1")
.with_metadata("key2", "value2");
assert_eq!(event.metadata.len(), 2);
assert_eq!(event.metadata.get("key1"), Some(&"value1".to_string()));
assert_eq!(event.metadata.get("key2"), Some(&"value2".to_string()));
}
}