1use serde::{Deserialize, Serialize};
7
8use super::scope::{AllocationEventType, ScopeEventType};
9use super::stats::TypeMemoryUsage;
10
11#[derive(Debug, Clone, Serialize)]
13pub struct TimelineData {
14 pub time_range: TimeRange,
16 pub allocation_events: Vec<AllocationEvent>,
18 pub scope_events: Vec<ScopeEvent>,
20 pub memory_snapshots: Vec<MemorySnapshot>,
22}
23
24#[derive(Debug, Clone, Serialize)]
26pub struct TimeRange {
27 pub start_time: u64,
29 pub end_time: u64,
31 pub duration_ms: u64,
33}
34
35#[derive(Debug, Clone, Serialize)]
37pub struct MemorySnapshot {
38 pub timestamp: u64,
40 pub total_memory: usize,
42 pub active_allocations: usize,
44 pub fragmentation_ratio: f64,
46 pub top_types: Vec<TypeMemoryUsage>,
48}
49
50#[derive(Debug, Clone, Serialize)]
52pub struct AllocationEvent {
53 pub timestamp: u64,
55 pub event_type: AllocationEventType,
57 pub ptr: usize,
59 pub size: usize,
61 pub var_name: Option<String>,
63 pub type_name: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize)]
69pub struct ScopeEvent {
70 pub timestamp: u64,
72 pub event_type: ScopeEventType,
74 pub scope_name: String,
76 pub memory_usage: usize,
78 pub variable_count: usize,
80}
81
82#[derive(Debug, Clone, Serialize)]
84pub struct StackTraceData {
85 pub hotspots: Vec<StackTraceHotspot>,
87 pub allocation_patterns: Vec<AllocationPattern>,
89 pub total_samples: usize,
91}
92
93#[derive(Debug, Clone, Serialize)]
95pub struct StackTraceHotspot {
96 pub function_name: String,
98 pub allocation_count: usize,
100 pub total_bytes: usize,
102 pub average_size: f64,
104 pub percentage: f64,
106}
107
108#[derive(Debug, Clone, Serialize)]
110pub struct AllocationPattern {
111 pub pattern_type: String,
113 pub frequency: usize,
115 pub total_bytes: usize,
117 pub description: String,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct StackFrame {
124 pub function_name: String,
126 pub file_name: Option<String>,
128 pub line_number: Option<u32>,
130 pub module_path: Option<String>,
132}
133
134#[derive(Debug, Clone, Serialize)]
136pub enum SafetyViolation {
137 PotentialLeak {
139 ptr: usize,
141 size: usize,
143 age_ms: u64,
145 description: String,
147 },
148 UseAfterFree {
150 ptr: usize,
152 description: String,
154 },
155 DoubleFree {
157 ptr: usize,
159 description: String,
161 },
162 BufferOverflow {
164 ptr: usize,
166 size: usize,
168 description: String,
170 },
171}
172
173#[derive(Debug, Clone, Serialize)]
175pub struct AllocationHotspot {
176 pub location: HotspotLocation,
178 pub allocation_count: usize,
180 pub total_bytes: usize,
182 pub average_size: f64,
184 pub frequency: f64,
186}
187
188#[derive(Debug, Clone, Serialize)]
190pub struct HotspotLocation {
191 pub function_name: String,
193 pub file_path: Option<String>,
195 pub line_number: Option<u32>,
197 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}