Skip to main content

memscope_rs/capture/types/
timeline.rs

1//! Timeline tracking types.
2//!
3//! This module contains types for timeline visualization,
4//! including memory snapshots, allocation events, and time ranges.
5
6use serde::{Deserialize, Serialize};
7
8use super::scope::{AllocationEventType, ScopeEventType};
9use super::stats::TypeMemoryUsage;
10
11/// Timeline data for visualization.
12#[derive(Debug, Clone, Serialize)]
13pub struct TimelineData {
14    /// Time range.
15    pub time_range: TimeRange,
16    /// Allocation events.
17    pub allocation_events: Vec<AllocationEvent>,
18    /// Scope events.
19    pub scope_events: Vec<ScopeEvent>,
20    /// Memory snapshots.
21    pub memory_snapshots: Vec<MemorySnapshot>,
22}
23
24/// Time range for timeline visualization.
25#[derive(Debug, Clone, Serialize)]
26pub struct TimeRange {
27    /// Start time.
28    pub start_time: u64,
29    /// End time.
30    pub end_time: u64,
31    /// Duration in milliseconds.
32    pub duration_ms: u64,
33}
34
35/// Memory snapshot at a point in time.
36#[derive(Debug, Clone, Serialize)]
37pub struct MemorySnapshot {
38    /// Timestamp.
39    pub timestamp: u64,
40    /// Total memory.
41    pub total_memory: usize,
42    /// Active allocations.
43    pub active_allocations: usize,
44    /// Fragmentation ratio.
45    pub fragmentation_ratio: f64,
46    /// Top types by memory usage.
47    pub top_types: Vec<TypeMemoryUsage>,
48}
49
50/// Allocation event for timeline.
51#[derive(Debug, Clone, Serialize)]
52pub struct AllocationEvent {
53    /// Timestamp.
54    pub timestamp: u64,
55    /// Event type.
56    pub event_type: AllocationEventType,
57    /// Memory pointer address.
58    pub ptr: usize,
59    /// Size in bytes.
60    pub size: usize,
61    /// Variable name.
62    pub var_name: Option<String>,
63    /// Type name.
64    pub type_name: Option<String>,
65}
66
67/// Scope event for timeline.
68#[derive(Debug, Clone, Serialize)]
69pub struct ScopeEvent {
70    /// Timestamp.
71    pub timestamp: u64,
72    /// Event type.
73    pub event_type: ScopeEventType,
74    /// Scope name.
75    pub scope_name: String,
76    /// Memory usage.
77    pub memory_usage: usize,
78    /// Variable count.
79    pub variable_count: usize,
80}
81
82/// Stack trace data for analysis.
83#[derive(Debug, Clone, Serialize)]
84pub struct StackTraceData {
85    /// Memory allocation hotspots.
86    pub hotspots: Vec<StackTraceHotspot>,
87    /// Detected allocation patterns.
88    pub allocation_patterns: Vec<AllocationPattern>,
89    /// Total number of samples.
90    pub total_samples: usize,
91}
92
93/// Stack trace hotspot.
94#[derive(Debug, Clone, Serialize)]
95pub struct StackTraceHotspot {
96    /// Function name.
97    pub function_name: String,
98    /// Number of allocations.
99    pub allocation_count: usize,
100    /// Total bytes allocated.
101    pub total_bytes: usize,
102    /// Average allocation size.
103    pub average_size: f64,
104    /// Percentage of total allocations.
105    pub percentage: f64,
106}
107
108/// Allocation pattern analysis.
109#[derive(Debug, Clone, Serialize)]
110pub struct AllocationPattern {
111    /// Pattern type.
112    pub pattern_type: String,
113    /// Frequency of occurrence.
114    pub frequency: usize,
115    /// Total bytes allocated.
116    pub total_bytes: usize,
117    /// Description.
118    pub description: String,
119}
120
121/// Stack frame for stack traces.
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct StackFrame {
124    /// Function name.
125    pub function_name: String,
126    /// Source file name.
127    pub file_name: Option<String>,
128    /// Line number in source code.
129    pub line_number: Option<u32>,
130    /// Module path.
131    pub module_path: Option<String>,
132}
133
134/// Safety violation types.
135#[derive(Debug, Clone, Serialize)]
136pub enum SafetyViolation {
137    /// Potential memory leak detected.
138    PotentialLeak {
139        /// Memory pointer.
140        ptr: usize,
141        /// Size in bytes.
142        size: usize,
143        /// Age in milliseconds.
144        age_ms: u64,
145        /// Description.
146        description: String,
147    },
148    /// Use after free violation detected.
149    UseAfterFree {
150        /// Memory pointer.
151        ptr: usize,
152        /// Description.
153        description: String,
154    },
155    /// Double free violation detected.
156    DoubleFree {
157        /// Memory pointer.
158        ptr: usize,
159        /// Description.
160        description: String,
161    },
162    /// Buffer overflow detected.
163    BufferOverflow {
164        /// Memory pointer.
165        ptr: usize,
166        /// Size in bytes.
167        size: usize,
168        /// Description.
169        description: String,
170    },
171}
172
173/// Allocation hotspot information.
174#[derive(Debug, Clone, Serialize)]
175pub struct AllocationHotspot {
176    /// Location information.
177    pub location: HotspotLocation,
178    /// Number of allocations.
179    pub allocation_count: usize,
180    /// Total bytes allocated.
181    pub total_bytes: usize,
182    /// Average allocation size.
183    pub average_size: f64,
184    /// Frequency of occurrence.
185    pub frequency: f64,
186}
187
188/// Hotspot location information.
189#[derive(Debug, Clone, Serialize)]
190pub struct HotspotLocation {
191    /// Function name.
192    pub function_name: String,
193    /// File path.
194    pub file_path: Option<String>,
195    /// Line number.
196    pub line_number: Option<u32>,
197    /// Module path.
198    pub module_path: Option<String>,
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_timeline_data() {
207        let timeline = TimelineData {
208            time_range: TimeRange {
209                start_time: 0,
210                end_time: 1000,
211                duration_ms: 1000,
212            },
213            allocation_events: vec![],
214            scope_events: vec![],
215            memory_snapshots: vec![],
216        };
217
218        assert_eq!(timeline.time_range.duration_ms, 1000);
219    }
220
221    #[test]
222    fn test_memory_snapshot() {
223        let snapshot = MemorySnapshot {
224            timestamp: 100,
225            total_memory: 1024 * 1024,
226            active_allocations: 10,
227            fragmentation_ratio: 0.1,
228            top_types: vec![],
229        };
230
231        assert_eq!(snapshot.active_allocations, 10);
232    }
233
234    #[test]
235    fn test_safety_violation() {
236        let violation = SafetyViolation::PotentialLeak {
237            ptr: 0x1000,
238            size: 1024,
239            age_ms: 5000,
240            description: "test leak".to_string(),
241        };
242
243        assert!(matches!(violation, SafetyViolation::PotentialLeak { .. }));
244    }
245
246    #[test]
247    fn test_time_range_creation() {
248        let range = TimeRange {
249            start_time: 0,
250            end_time: 10000,
251            duration_ms: 10000,
252        };
253
254        assert_eq!(range.start_time, 0);
255        assert_eq!(range.end_time, 10000);
256        assert_eq!(range.duration_ms, 10000);
257    }
258
259    #[test]
260    fn test_allocation_event_creation() {
261        let event = AllocationEvent {
262            timestamp: 1000,
263            event_type: AllocationEventType::Allocate,
264            ptr: 0x1000,
265            size: 1024,
266            var_name: Some("buffer".to_string()),
267            type_name: Some("Vec<u8>".to_string()),
268        };
269
270        assert_eq!(event.timestamp, 1000);
271        assert_eq!(event.event_type, AllocationEventType::Allocate);
272        assert_eq!(event.size, 1024);
273    }
274
275    #[test]
276    fn test_scope_event_creation() {
277        let event = ScopeEvent {
278            timestamp: 500,
279            event_type: ScopeEventType::Enter,
280            scope_name: "main".to_string(),
281            memory_usage: 2048,
282            variable_count: 5,
283        };
284
285        assert_eq!(event.event_type, ScopeEventType::Enter);
286        assert_eq!(event.scope_name, "main");
287        assert_eq!(event.variable_count, 5);
288    }
289
290    #[test]
291    fn test_stack_trace_data_creation() {
292        let data = StackTraceData {
293            hotspots: vec![],
294            allocation_patterns: vec![],
295            total_samples: 100,
296        };
297
298        assert_eq!(data.total_samples, 100);
299        assert!(data.hotspots.is_empty());
300    }
301
302    #[test]
303    fn test_stack_trace_hotspot_creation() {
304        let hotspot = StackTraceHotspot {
305            function_name: "allocate_buffer".to_string(),
306            allocation_count: 50,
307            total_bytes: 10240,
308            average_size: 204.8,
309            percentage: 25.0,
310        };
311
312        assert_eq!(hotspot.function_name, "allocate_buffer");
313        assert_eq!(hotspot.allocation_count, 50);
314    }
315
316    #[test]
317    fn test_allocation_pattern_creation() {
318        let pattern = AllocationPattern {
319            pattern_type: "repeated".to_string(),
320            frequency: 100,
321            total_bytes: 4096,
322            description: "Repeated small allocations".to_string(),
323        };
324
325        assert_eq!(pattern.pattern_type, "repeated");
326        assert_eq!(pattern.frequency, 100);
327    }
328
329    #[test]
330    fn test_stack_frame_creation() {
331        let frame = StackFrame {
332            function_name: "main".to_string(),
333            file_name: Some("main.rs".to_string()),
334            line_number: Some(42),
335            module_path: Some("myapp".to_string()),
336        };
337
338        assert_eq!(frame.function_name, "main");
339        assert_eq!(frame.line_number, Some(42));
340    }
341
342    #[test]
343    fn test_stack_frame_minimal() {
344        let frame = StackFrame {
345            function_name: "unknown".to_string(),
346            file_name: None,
347            line_number: None,
348            module_path: None,
349        };
350
351        assert_eq!(frame.function_name, "unknown");
352        assert!(frame.file_name.is_none());
353    }
354
355    #[test]
356    fn test_safety_violation_use_after_free() {
357        let violation = SafetyViolation::UseAfterFree {
358            ptr: 0x2000,
359            description: "Access after free".to_string(),
360        };
361
362        assert!(matches!(violation, SafetyViolation::UseAfterFree { .. }));
363    }
364
365    #[test]
366    fn test_safety_violation_double_free() {
367        let violation = SafetyViolation::DoubleFree {
368            ptr: 0x3000,
369            description: "Double free detected".to_string(),
370        };
371
372        assert!(matches!(violation, SafetyViolation::DoubleFree { .. }));
373    }
374
375    #[test]
376    fn test_safety_violation_buffer_overflow() {
377        let violation = SafetyViolation::BufferOverflow {
378            ptr: 0x4000,
379            size: 1024,
380            description: "Buffer overflow".to_string(),
381        };
382
383        assert!(matches!(violation, SafetyViolation::BufferOverflow { .. }));
384    }
385
386    #[test]
387    fn test_allocation_hotspot_creation() {
388        let hotspot = AllocationHotspot {
389            location: HotspotLocation {
390                function_name: "process_data".to_string(),
391                file_path: Some("processor.rs".to_string()),
392                line_number: Some(100),
393                module_path: None,
394            },
395            allocation_count: 200,
396            total_bytes: 8192,
397            average_size: 40.96,
398            frequency: 0.5,
399        };
400
401        assert_eq!(hotspot.allocation_count, 200);
402        assert_eq!(hotspot.location.function_name, "process_data");
403    }
404
405    #[test]
406    fn test_hotspot_location_creation() {
407        let location = HotspotLocation {
408            function_name: "test_fn".to_string(),
409            file_path: Some("test.rs".to_string()),
410            line_number: Some(10),
411            module_path: Some("test_module".to_string()),
412        };
413
414        assert_eq!(location.function_name, "test_fn");
415        assert_eq!(location.line_number, Some(10));
416    }
417
418    #[test]
419    fn test_timeline_data_with_events() {
420        let alloc_event = AllocationEvent {
421            timestamp: 100,
422            event_type: AllocationEventType::Allocate,
423            ptr: 0x1000,
424            size: 512,
425            var_name: None,
426            type_name: None,
427        };
428
429        let scope_event = ScopeEvent {
430            timestamp: 50,
431            event_type: ScopeEventType::Enter,
432            scope_name: "test".to_string(),
433            memory_usage: 0,
434            variable_count: 0,
435        };
436
437        let timeline = TimelineData {
438            time_range: TimeRange {
439                start_time: 0,
440                end_time: 1000,
441                duration_ms: 1000,
442            },
443            allocation_events: vec![alloc_event],
444            scope_events: vec![scope_event],
445            memory_snapshots: vec![],
446        };
447
448        assert_eq!(timeline.allocation_events.len(), 1);
449        assert_eq!(timeline.scope_events.len(), 1);
450    }
451
452    #[test]
453    fn test_memory_snapshot_with_types() {
454        let type_usage = TypeMemoryUsage {
455            type_name: "String".to_string(),
456            total_size: 4096,
457            allocation_count: 100,
458            average_size: 40.96,
459            peak_size: 5000,
460            current_size: 4096,
461            efficiency_score: 0.9,
462        };
463
464        let snapshot = MemorySnapshot {
465            timestamp: 500,
466            total_memory: 8192,
467            active_allocations: 50,
468            fragmentation_ratio: 0.15,
469            top_types: vec![type_usage],
470        };
471
472        assert_eq!(snapshot.top_types.len(), 1);
473        assert_eq!(snapshot.top_types[0].type_name, "String");
474    }
475
476    #[test]
477    fn test_stack_trace_data_with_hotspots() {
478        let hotspot = StackTraceHotspot {
479            function_name: "hot_function".to_string(),
480            allocation_count: 500,
481            total_bytes: 20480,
482            average_size: 40.96,
483            percentage: 50.0,
484        };
485
486        let pattern = AllocationPattern {
487            pattern_type: "burst".to_string(),
488            frequency: 10,
489            total_bytes: 1024,
490            description: "Burst allocation pattern".to_string(),
491        };
492
493        let data = StackTraceData {
494            hotspots: vec![hotspot],
495            allocation_patterns: vec![pattern],
496            total_samples: 1000,
497        };
498
499        assert_eq!(data.hotspots.len(), 1);
500        assert_eq!(data.allocation_patterns.len(), 1);
501    }
502}