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)]
42pub struct MemorySnapshot {
43 pub timestamp: u64,
45 pub total_memory: usize,
47 pub active_allocations: usize,
49 pub fragmentation_ratio: f64,
51 pub top_types: Vec<TypeMemoryUsage>,
53}
54
55#[derive(Debug, Clone, Serialize)]
57pub struct AllocationEvent {
58 pub timestamp: u64,
60 pub event_type: AllocationEventType,
62 pub ptr: usize,
64 pub size: usize,
66 pub var_name: Option<String>,
68 pub type_name: Option<String>,
70}
71
72#[derive(Debug, Clone, Serialize)]
74pub struct ScopeEvent {
75 pub timestamp: u64,
77 pub event_type: ScopeEventType,
79 pub scope_name: String,
81 pub memory_usage: usize,
83 pub variable_count: usize,
85}
86
87#[derive(Debug, Clone, Serialize)]
89pub struct StackTraceData {
90 pub hotspots: Vec<StackTraceHotspot>,
92 pub allocation_patterns: Vec<AllocationPattern>,
94 pub total_samples: usize,
96}
97
98#[derive(Debug, Clone, Serialize)]
100pub struct StackTraceHotspot {
101 pub function_name: String,
103 pub allocation_count: usize,
105 pub total_bytes: usize,
107 pub average_size: f64,
109 pub percentage: f64,
111}
112
113#[derive(Debug, Clone, Serialize)]
115pub struct AllocationPattern {
116 pub pattern_type: String,
118 pub frequency: usize,
120 pub total_bytes: usize,
122 pub description: String,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct StackFrame {
129 pub function_name: String,
131 pub file_name: Option<String>,
133 pub line_number: Option<u32>,
135 pub module_path: Option<String>,
137}
138
139#[derive(Debug, Clone, Serialize)]
141pub enum SafetyViolation {
142 PotentialLeak {
144 ptr: usize,
146 size: usize,
148 age_ms: u64,
150 description: String,
152 },
153 UseAfterFree {
155 ptr: usize,
157 description: String,
159 },
160 DoubleFree {
162 ptr: usize,
164 description: String,
166 },
167 BufferOverflow {
169 ptr: usize,
171 size: usize,
173 description: String,
175 },
176}
177
178#[derive(Debug, Clone, Serialize)]
180pub struct AllocationHotspot {
181 pub location: HotspotLocation,
183 pub allocation_count: usize,
185 pub total_bytes: usize,
187 pub average_size: f64,
189 pub frequency: f64,
191}
192
193#[derive(Debug, Clone, Serialize)]
195pub struct HotspotLocation {
196 pub function_name: String,
198 pub file_path: Option<String>,
200 pub line_number: Option<u32>,
202 pub module_path: Option<String>,
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_timeline_data() {
212 let timeline = TimelineData {
213 time_range: TimeRange {
214 start_time: 0,
215 end_time: 1000,
216 duration_ms: 1000,
217 },
218 allocation_events: vec![],
219 scope_events: vec![],
220 memory_snapshots: vec![],
221 };
222
223 assert_eq!(timeline.time_range.duration_ms, 1000);
224 }
225
226 #[test]
227 fn test_memory_snapshot() {
228 let snapshot = MemorySnapshot {
229 timestamp: 100,
230 total_memory: 1024 * 1024,
231 active_allocations: 10,
232 fragmentation_ratio: 0.1,
233 top_types: vec![],
234 };
235
236 assert_eq!(snapshot.active_allocations, 10);
237 }
238
239 #[test]
240 fn test_safety_violation() {
241 let violation = SafetyViolation::PotentialLeak {
242 ptr: 0x1000,
243 size: 1024,
244 age_ms: 5000,
245 description: "test leak".to_string(),
246 };
247
248 assert!(matches!(violation, SafetyViolation::PotentialLeak { .. }));
249 }
250
251 #[test]
252 fn test_time_range_creation() {
253 let range = TimeRange {
254 start_time: 0,
255 end_time: 10000,
256 duration_ms: 10000,
257 };
258
259 assert_eq!(range.start_time, 0);
260 assert_eq!(range.end_time, 10000);
261 assert_eq!(range.duration_ms, 10000);
262 }
263
264 #[test]
265 fn test_allocation_event_creation() {
266 let event = AllocationEvent {
267 timestamp: 1000,
268 event_type: AllocationEventType::Allocate,
269 ptr: 0x1000,
270 size: 1024,
271 var_name: Some("buffer".to_string()),
272 type_name: Some("Vec<u8>".to_string()),
273 };
274
275 assert_eq!(event.timestamp, 1000);
276 assert_eq!(event.event_type, AllocationEventType::Allocate);
277 assert_eq!(event.size, 1024);
278 }
279
280 #[test]
281 fn test_scope_event_creation() {
282 let event = ScopeEvent {
283 timestamp: 500,
284 event_type: ScopeEventType::Enter,
285 scope_name: "main".to_string(),
286 memory_usage: 2048,
287 variable_count: 5,
288 };
289
290 assert_eq!(event.event_type, ScopeEventType::Enter);
291 assert_eq!(event.scope_name, "main");
292 assert_eq!(event.variable_count, 5);
293 }
294
295 #[test]
296 fn test_stack_trace_data_creation() {
297 let data = StackTraceData {
298 hotspots: vec![],
299 allocation_patterns: vec![],
300 total_samples: 100,
301 };
302
303 assert_eq!(data.total_samples, 100);
304 assert!(data.hotspots.is_empty());
305 }
306
307 #[test]
308 fn test_stack_trace_hotspot_creation() {
309 let hotspot = StackTraceHotspot {
310 function_name: "allocate_buffer".to_string(),
311 allocation_count: 50,
312 total_bytes: 10240,
313 average_size: 204.8,
314 percentage: 25.0,
315 };
316
317 assert_eq!(hotspot.function_name, "allocate_buffer");
318 assert_eq!(hotspot.allocation_count, 50);
319 }
320
321 #[test]
322 fn test_allocation_pattern_creation() {
323 let pattern = AllocationPattern {
324 pattern_type: "repeated".to_string(),
325 frequency: 100,
326 total_bytes: 4096,
327 description: "Repeated small allocations".to_string(),
328 };
329
330 assert_eq!(pattern.pattern_type, "repeated");
331 assert_eq!(pattern.frequency, 100);
332 }
333
334 #[test]
335 fn test_stack_frame_creation() {
336 let frame = StackFrame {
337 function_name: "main".to_string(),
338 file_name: Some("main.rs".to_string()),
339 line_number: Some(42),
340 module_path: Some("myapp".to_string()),
341 };
342
343 assert_eq!(frame.function_name, "main");
344 assert_eq!(frame.line_number, Some(42));
345 }
346
347 #[test]
348 fn test_stack_frame_minimal() {
349 let frame = StackFrame {
350 function_name: "unknown".to_string(),
351 file_name: None,
352 line_number: None,
353 module_path: None,
354 };
355
356 assert_eq!(frame.function_name, "unknown");
357 assert!(frame.file_name.is_none());
358 }
359
360 #[test]
361 fn test_safety_violation_use_after_free() {
362 let violation = SafetyViolation::UseAfterFree {
363 ptr: 0x2000,
364 description: "Access after free".to_string(),
365 };
366
367 assert!(matches!(violation, SafetyViolation::UseAfterFree { .. }));
368 }
369
370 #[test]
371 fn test_safety_violation_double_free() {
372 let violation = SafetyViolation::DoubleFree {
373 ptr: 0x3000,
374 description: "Double free detected".to_string(),
375 };
376
377 assert!(matches!(violation, SafetyViolation::DoubleFree { .. }));
378 }
379
380 #[test]
381 fn test_safety_violation_buffer_overflow() {
382 let violation = SafetyViolation::BufferOverflow {
383 ptr: 0x4000,
384 size: 1024,
385 description: "Buffer overflow".to_string(),
386 };
387
388 assert!(matches!(violation, SafetyViolation::BufferOverflow { .. }));
389 }
390
391 #[test]
392 fn test_allocation_hotspot_creation() {
393 let hotspot = AllocationHotspot {
394 location: HotspotLocation {
395 function_name: "process_data".to_string(),
396 file_path: Some("processor.rs".to_string()),
397 line_number: Some(100),
398 module_path: None,
399 },
400 allocation_count: 200,
401 total_bytes: 8192,
402 average_size: 40.96,
403 frequency: 0.5,
404 };
405
406 assert_eq!(hotspot.allocation_count, 200);
407 assert_eq!(hotspot.location.function_name, "process_data");
408 }
409
410 #[test]
411 fn test_hotspot_location_creation() {
412 let location = HotspotLocation {
413 function_name: "test_fn".to_string(),
414 file_path: Some("test.rs".to_string()),
415 line_number: Some(10),
416 module_path: Some("test_module".to_string()),
417 };
418
419 assert_eq!(location.function_name, "test_fn");
420 assert_eq!(location.line_number, Some(10));
421 }
422
423 #[test]
424 fn test_timeline_data_with_events() {
425 let alloc_event = AllocationEvent {
426 timestamp: 100,
427 event_type: AllocationEventType::Allocate,
428 ptr: 0x1000,
429 size: 512,
430 var_name: None,
431 type_name: None,
432 };
433
434 let scope_event = ScopeEvent {
435 timestamp: 50,
436 event_type: ScopeEventType::Enter,
437 scope_name: "test".to_string(),
438 memory_usage: 0,
439 variable_count: 0,
440 };
441
442 let timeline = TimelineData {
443 time_range: TimeRange {
444 start_time: 0,
445 end_time: 1000,
446 duration_ms: 1000,
447 },
448 allocation_events: vec![alloc_event],
449 scope_events: vec![scope_event],
450 memory_snapshots: vec![],
451 };
452
453 assert_eq!(timeline.allocation_events.len(), 1);
454 assert_eq!(timeline.scope_events.len(), 1);
455 }
456
457 #[test]
458 fn test_memory_snapshot_with_types() {
459 let type_usage = TypeMemoryUsage {
460 type_name: "String".to_string(),
461 total_size: 4096,
462 allocation_count: 100,
463 average_size: 40.96,
464 peak_size: 5000,
465 current_size: 4096,
466 efficiency_score: 0.9,
467 };
468
469 let snapshot = MemorySnapshot {
470 timestamp: 500,
471 total_memory: 8192,
472 active_allocations: 50,
473 fragmentation_ratio: 0.15,
474 top_types: vec![type_usage],
475 };
476
477 assert_eq!(snapshot.top_types.len(), 1);
478 assert_eq!(snapshot.top_types[0].type_name, "String");
479 }
480
481 #[test]
482 fn test_stack_trace_data_with_hotspots() {
483 let hotspot = StackTraceHotspot {
484 function_name: "hot_function".to_string(),
485 allocation_count: 500,
486 total_bytes: 20480,
487 average_size: 40.96,
488 percentage: 50.0,
489 };
490
491 let pattern = AllocationPattern {
492 pattern_type: "burst".to_string(),
493 frequency: 10,
494 total_bytes: 1024,
495 description: "Burst allocation pattern".to_string(),
496 };
497
498 let data = StackTraceData {
499 hotspots: vec![hotspot],
500 allocation_patterns: vec![pattern],
501 total_samples: 1000,
502 };
503
504 assert_eq!(data.hotspots.len(), 1);
505 assert_eq!(data.allocation_patterns.len(), 1);
506 }
507}