scirs2_core/memory/metrics/
event.rs

1//! Memory event tracking system
2//!
3//! This module provides types for tracking memory-related events such as
4//! allocations, deallocations, and accesses.
5
6use std::collections::HashMap;
7use std::fmt;
8use std::thread::ThreadId;
9use std::time::Instant;
10
11/// Type of memory event
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum MemoryEventType {
14    /// Memory allocation
15    Allocation,
16    /// Memory deallocation
17    Deallocation,
18    /// Memory resize
19    Resize,
20    /// Memory access
21    Access,
22    /// Memory transfer (e.g., between CPU and GPU)
23    Transfer,
24}
25
26impl fmt::Display for MemoryEventType {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match self {
29            MemoryEventType::Allocation => write!(f, "Allocation"),
30            MemoryEventType::Deallocation => write!(f, "Deallocation"),
31            MemoryEventType::Resize => write!(f, "Resize"),
32            MemoryEventType::Access => write!(f, "Access"),
33            MemoryEventType::Transfer => write!(f, "Transfer"),
34        }
35    }
36}
37
38/// A memory event representing an allocation, deallocation, etc.
39#[derive(Debug, Clone)]
40pub struct MemoryEvent {
41    /// Event type
42    pub event_type: MemoryEventType,
43    /// Component that generated the event (e.g., "BufferPool", "ChunkProcessor")
44    pub component: String,
45    /// Optional operation context (e.g., "matrix_multiply", "fft")
46    pub context: Option<String>,
47    /// Allocation size in bytes
48    pub size: usize,
49    /// Memory address (for correlation)
50    pub address: usize,
51    /// Thread ID
52    pub thread_id: ThreadId,
53    /// Timestamp
54    pub timestamp: Instant,
55    /// Call stack (if enabled)
56    pub call_stack: Option<Vec<String>>,
57    /// Additional metadata
58    pub metadata: HashMap<String, String>,
59}
60
61impl MemoryEvent {
62    /// Create a new memory event
63    pub fn new(
64        event_type: MemoryEventType,
65        component: impl Into<String>,
66        size: usize,
67        address: usize,
68    ) -> Self {
69        Self {
70            event_type,
71            component: component.into(),
72            context: None,
73            size,
74            address,
75            thread_id: std::thread::current().id(),
76            timestamp: Instant::now(),
77            call_stack: None,
78            metadata: HashMap::new(),
79        }
80    }
81
82    /// Add context to the event
83    pub fn with_context(mut self, context: impl Into<String>) -> Self {
84        self.context = Some(context.into());
85        self
86    }
87
88    /// Add call stack to the event (if enabled)
89    pub fn with_call_stack(mut self) -> Self {
90        if cfg!(feature = "memory_call_stack") {
91            self.call_stack = Some(capture_call_stack(3)); // Skip 3 frames
92        }
93        self
94    }
95
96    /// Add metadata to the event
97    pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
98        self.metadata.insert(key.into(), value.into());
99        self
100    }
101}
102
103/// Capture current call stack (simplified implementation)
104fn capture_call_stack(skipframes: usize) -> Vec<String> {
105    // In a real implementation, this would use backtrace crate or similar
106    vec![format!("frame_{}", skipframes)]
107}
108
109/// Capture a call stack (simplified implementation)
110#[cfg(feature = "memory_call_stack")]
111#[allow(dead_code)]
112fn capture_stack_frames(maxframes: usize) -> Vec<String> {
113    // This is a placeholder. In a real implementation, we would use
114    // the backtrace crate to capture the call stack.
115    vec!["<callstack not available>".to_string()]
116}
117
118#[cfg(not(feature = "memory_call_stack"))]
119#[allow(dead_code)]
120fn capture_stack_frames(maxframes: usize) -> Vec<String> {
121    Vec::new()
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_memory_event_creation() {
130        let event = MemoryEvent::new(
131            MemoryEventType::Allocation,
132            "TestComponent",
133            1024,
134            0xdeadbeef,
135        );
136
137        assert_eq!(event.event_type, MemoryEventType::Allocation);
138        assert_eq!(event.component, "TestComponent");
139        assert_eq!(event.size, 1024);
140        assert_eq!(event.address, 0xdeadbeef);
141        assert_eq!(event.context, None);
142        assert!(event.call_stack.is_none());
143        assert!(event.metadata.is_empty());
144    }
145
146    #[test]
147    fn test_memory_event_with_context() {
148        let event = MemoryEvent::new(
149            MemoryEventType::Allocation,
150            "TestComponent",
151            1024,
152            0xdeadbeef,
153        )
154        .with_context("TestContext");
155
156        assert_eq!(event.context, Some("TestContext".to_string()));
157    }
158
159    #[test]
160    fn test_memory_event_with_metadata() {
161        let event = MemoryEvent::new(
162            MemoryEventType::Allocation,
163            "TestComponent",
164            1024,
165            0xdeadbeef,
166        )
167        .with_metadata("key1", "value1")
168        .with_metadata("key2", "value2");
169
170        assert_eq!(event.metadata.len(), 2);
171        assert_eq!(event.metadata.get("key1"), Some(&"value1".to_string()));
172        assert_eq!(event.metadata.get("key2"), Some(&"value2".to_string()));
173    }
174}