1use bincode::{Decode, Encode};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
12pub struct ThreadStats {
13 pub thread_id: u64,
15 pub total_allocations: u64,
17 pub total_deallocations: u64,
19 pub peak_memory: usize,
21 pub total_allocated: usize,
23 pub allocation_frequency: HashMap<u64, u64>,
25 pub avg_allocation_size: f64,
27 pub timeline: Vec<AllocationEvent>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
33pub struct AllocationEvent {
34 pub timestamp: u64,
36 pub ptr: usize,
38 pub size: usize,
40 pub call_stack_hash: u64,
42 pub event_type: EventType,
44 pub thread_id: u64,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Encode, Decode)]
50pub enum EventType {
51 Allocation,
53 Deallocation,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
59pub struct LockfreeAnalysis {
60 pub thread_stats: HashMap<u64, ThreadStats>,
62 pub hottest_call_stacks: Vec<HotCallStack>,
64 pub thread_interactions: Vec<ThreadInteraction>,
66 pub memory_peaks: Vec<MemoryPeak>,
68 pub performance_bottlenecks: Vec<PerformanceBottleneck>,
70 pub summary: AnalysisSummary,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
76pub struct HotCallStack {
77 pub call_stack_hash: u64,
79 pub total_frequency: u64,
81 pub total_size: usize,
83 pub impact_score: u64,
85 pub threads: Vec<u64>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
91pub struct ThreadInteraction {
92 pub thread_a: u64,
94 pub thread_b: u64,
96 pub shared_patterns: Vec<u64>,
98 pub interaction_strength: u64,
100 pub interaction_type: InteractionType,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
106pub enum InteractionType {
107 SimilarPatterns,
109 MemorySharing,
111 ProducerConsumer,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
117pub struct MemoryPeak {
118 pub timestamp: u64,
120 pub thread_id: u64,
122 pub memory_usage: usize,
124 pub active_allocations: u64,
126 pub triggering_call_stack: u64,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
132pub struct PerformanceBottleneck {
133 pub bottleneck_type: BottleneckType,
135 pub thread_id: u64,
137 pub call_stack_hash: u64,
139 pub severity: f64,
141 pub description: String,
143 pub suggestion: String,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
149pub enum BottleneckType {
150 HighFrequencySmallAllocation,
152 LargeAllocationSpike,
154 MemoryLeak,
156 ThreadContention,
158 FragmentationRisk,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
164pub struct AnalysisSummary {
165 pub total_threads: usize,
167 pub total_allocations: u64,
169 pub total_deallocations: u64,
171 pub peak_memory_usage: usize,
173 pub total_memory_allocated: usize,
175 pub unique_call_stacks: usize,
177 pub analysis_duration_ms: u64,
179 pub sampling_effectiveness: f64,
181}
182
183impl LockfreeAnalysis {
184 pub fn new() -> Self {
186 Self {
187 thread_stats: HashMap::new(),
188 hottest_call_stacks: Vec::new(),
189 thread_interactions: Vec::new(),
190 memory_peaks: Vec::new(),
191 performance_bottlenecks: Vec::new(),
192 summary: AnalysisSummary {
193 total_threads: 0,
194 total_allocations: 0,
195 total_deallocations: 0,
196 peak_memory_usage: 0,
197 total_memory_allocated: 0,
198 unique_call_stacks: 0,
199 analysis_duration_ms: 0,
200 sampling_effectiveness: 0.0,
201 },
202 }
203 }
204
205 pub fn calculate_summary(&mut self, analysis_start: std::time::Instant) {
207 let mut total_allocations = 0;
208 let mut total_deallocations = 0;
209 let mut peak_memory = 0;
210 let mut total_allocated = 0;
211 let mut all_call_stacks = std::collections::HashSet::new();
212
213 for stats in self.thread_stats.values() {
214 total_allocations += stats.total_allocations;
215 total_deallocations += stats.total_deallocations;
216 peak_memory += stats.peak_memory; total_allocated += stats.total_allocated;
218
219 for &call_stack_hash in stats.allocation_frequency.keys() {
220 all_call_stacks.insert(call_stack_hash);
221 }
222 }
223
224 self.summary = AnalysisSummary {
225 total_threads: self.thread_stats.len(),
226 total_allocations,
227 total_deallocations,
228 peak_memory_usage: peak_memory,
229 total_memory_allocated: total_allocated,
230 unique_call_stacks: all_call_stacks.len(),
231 analysis_duration_ms: analysis_start.elapsed().as_millis() as u64,
232 sampling_effectiveness: if total_allocations > 0 {
233 100.0 } else {
236 0.0
237 },
238 };
239 }
240
241 pub fn get_most_active_threads(&self, limit: usize) -> Vec<(u64, u64)> {
243 let mut thread_activity: Vec<_> = self
244 .thread_stats
245 .iter()
246 .map(|(&thread_id, stats)| (thread_id, stats.total_allocations))
247 .collect();
248
249 thread_activity.sort_by(|a, b| b.1.cmp(&a.1));
250 thread_activity.truncate(limit);
251 thread_activity
252 }
253
254 pub fn get_highest_memory_threads(&self, limit: usize) -> Vec<(u64, usize)> {
256 let mut thread_memory: Vec<_> = self
257 .thread_stats
258 .iter()
259 .map(|(&thread_id, stats)| (thread_id, stats.peak_memory))
260 .collect();
261
262 thread_memory.sort_by(|a, b| b.1.cmp(&a.1));
263 thread_memory.truncate(limit);
264 thread_memory
265 }
266
267 pub fn get_critical_bottlenecks(&self, severity_threshold: f64) -> Vec<&PerformanceBottleneck> {
269 self.performance_bottlenecks
270 .iter()
271 .filter(|b| b.severity >= severity_threshold)
272 .collect()
273 }
274}
275
276impl Default for LockfreeAnalysis {
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285 use std::time::Instant;
286
287 fn create_test_thread_stats(
288 thread_id: u64,
289 allocations: u64,
290 peak_memory: usize,
291 ) -> ThreadStats {
292 let mut allocation_frequency = HashMap::new();
293 allocation_frequency.insert(12345, allocations / 2);
294 allocation_frequency.insert(67890, allocations / 2);
295
296 ThreadStats {
297 thread_id,
298 total_allocations: allocations,
299 total_deallocations: allocations - 1, peak_memory,
301 total_allocated: peak_memory,
302 allocation_frequency,
303 avg_allocation_size: peak_memory as f64 / allocations as f64,
304 timeline: vec![
305 AllocationEvent {
306 timestamp: 1000,
307 ptr: 0x1000,
308 size: 1024,
309 call_stack_hash: 12345,
310 event_type: EventType::Allocation,
311 thread_id,
312 },
313 AllocationEvent {
314 timestamp: 2000,
315 ptr: 0x2000,
316 size: 2048,
317 call_stack_hash: 67890,
318 event_type: EventType::Allocation,
319 thread_id,
320 },
321 ],
322 }
323 }
324
325 #[test]
326 fn test_lockfree_analysis_creation() {
327 let analysis = LockfreeAnalysis::new();
328
329 assert!(analysis.thread_stats.is_empty());
330 assert!(analysis.hottest_call_stacks.is_empty());
331 assert!(analysis.thread_interactions.is_empty());
332 assert!(analysis.memory_peaks.is_empty());
333 assert!(analysis.performance_bottlenecks.is_empty());
334 assert_eq!(analysis.summary.total_threads, 0);
335 assert_eq!(analysis.summary.total_allocations, 0);
336 }
337
338 #[test]
339 fn test_lockfree_analysis_default() {
340 let analysis = LockfreeAnalysis::default();
341 assert!(analysis.thread_stats.is_empty());
342 assert_eq!(analysis.summary.total_threads, 0);
343 }
344
345 #[test]
346 fn test_calculate_summary_single_thread() {
347 let mut analysis = LockfreeAnalysis::new();
348 let start_time = Instant::now();
349
350 let thread_stats = create_test_thread_stats(1, 100, 8192);
352 analysis.thread_stats.insert(1, thread_stats);
353
354 analysis.calculate_summary(start_time);
355
356 assert_eq!(analysis.summary.total_threads, 1);
357 assert_eq!(analysis.summary.total_allocations, 100);
358 assert_eq!(analysis.summary.total_deallocations, 99);
359 assert_eq!(analysis.summary.peak_memory_usage, 8192); assert_eq!(analysis.summary.total_memory_allocated, 8192);
361 assert_eq!(analysis.summary.unique_call_stacks, 2); }
364
365 #[test]
366 fn test_calculate_summary_multiple_threads() {
367 let mut analysis = LockfreeAnalysis::new();
368 let start_time = Instant::now();
369
370 analysis
372 .thread_stats
373 .insert(1, create_test_thread_stats(1, 100, 4096));
374 analysis
375 .thread_stats
376 .insert(2, create_test_thread_stats(2, 50, 2048));
377 analysis
378 .thread_stats
379 .insert(3, create_test_thread_stats(3, 200, 8192));
380
381 analysis.calculate_summary(start_time);
382
383 assert_eq!(analysis.summary.total_threads, 3);
384 assert_eq!(analysis.summary.total_allocations, 350); assert_eq!(analysis.summary.total_deallocations, 347); assert_eq!(analysis.summary.peak_memory_usage, 14336); assert_eq!(analysis.summary.total_memory_allocated, 14336); assert_eq!(analysis.summary.unique_call_stacks, 2); }
390
391 #[test]
392 fn test_get_most_active_threads() {
393 let mut analysis = LockfreeAnalysis::new();
394
395 analysis
396 .thread_stats
397 .insert(1, create_test_thread_stats(1, 100, 4096));
398 analysis
399 .thread_stats
400 .insert(2, create_test_thread_stats(2, 300, 2048));
401 analysis
402 .thread_stats
403 .insert(3, create_test_thread_stats(3, 50, 8192));
404
405 let most_active = analysis.get_most_active_threads(2);
406
407 assert_eq!(most_active.len(), 2);
408 assert_eq!(most_active[0], (2, 300)); assert_eq!(most_active[1], (1, 100)); }
411
412 #[test]
413 fn test_get_most_active_threads_empty() {
414 let analysis = LockfreeAnalysis::new();
415 let most_active = analysis.get_most_active_threads(5);
416 assert!(most_active.is_empty());
417 }
418
419 #[test]
420 fn test_get_highest_memory_threads() {
421 let mut analysis = LockfreeAnalysis::new();
422
423 analysis
424 .thread_stats
425 .insert(1, create_test_thread_stats(1, 100, 4096));
426 analysis
427 .thread_stats
428 .insert(2, create_test_thread_stats(2, 300, 2048));
429 analysis
430 .thread_stats
431 .insert(3, create_test_thread_stats(3, 50, 8192));
432
433 let highest_memory = analysis.get_highest_memory_threads(2);
434
435 assert_eq!(highest_memory.len(), 2);
436 assert_eq!(highest_memory[0], (3, 8192)); assert_eq!(highest_memory[1], (1, 4096)); }
439
440 #[test]
441 fn test_get_critical_bottlenecks() {
442 let mut analysis = LockfreeAnalysis::new();
443
444 analysis
445 .performance_bottlenecks
446 .push(PerformanceBottleneck {
447 bottleneck_type: BottleneckType::HighFrequencySmallAllocation,
448 thread_id: 1,
449 call_stack_hash: 12345,
450 severity: 0.8,
451 description: "High frequency allocations detected".to_string(),
452 suggestion: "Consider using a memory pool".to_string(),
453 });
454
455 analysis
456 .performance_bottlenecks
457 .push(PerformanceBottleneck {
458 bottleneck_type: BottleneckType::MemoryLeak,
459 thread_id: 2,
460 call_stack_hash: 67890,
461 severity: 0.3,
462 description: "Potential memory leak".to_string(),
463 suggestion: "Check deallocation patterns".to_string(),
464 });
465
466 let critical = analysis.get_critical_bottlenecks(0.7);
467 assert_eq!(critical.len(), 1);
468 assert_eq!(critical[0].severity, 0.8);
469
470 let all_bottlenecks = analysis.get_critical_bottlenecks(0.0);
471 assert_eq!(all_bottlenecks.len(), 2);
472 }
473
474 #[test]
475 fn test_event_type_equality() {
476 assert_eq!(EventType::Allocation, EventType::Allocation);
477 assert_eq!(EventType::Deallocation, EventType::Deallocation);
478 assert_ne!(EventType::Allocation, EventType::Deallocation);
479 }
480
481 #[test]
482 fn test_allocation_event_creation() {
483 let event = AllocationEvent {
484 timestamp: 12345,
485 ptr: 0x1000,
486 size: 1024,
487 call_stack_hash: 67890,
488 event_type: EventType::Allocation,
489 thread_id: 1,
490 };
491
492 assert_eq!(event.timestamp, 12345);
493 assert_eq!(event.ptr, 0x1000);
494 assert_eq!(event.size, 1024);
495 assert_eq!(event.call_stack_hash, 67890);
496 assert_eq!(event.event_type, EventType::Allocation);
497 assert_eq!(event.thread_id, 1);
498 }
499
500 #[test]
501 fn test_thread_stats_creation() {
502 let stats = create_test_thread_stats(42, 1000, 16384);
503
504 assert_eq!(stats.thread_id, 42);
505 assert_eq!(stats.total_allocations, 1000);
506 assert_eq!(stats.total_deallocations, 999);
507 assert_eq!(stats.peak_memory, 16384);
508 assert_eq!(stats.total_allocated, 16384);
509 assert_eq!(stats.avg_allocation_size, 16.384);
510 assert_eq!(stats.allocation_frequency.len(), 2);
511 assert_eq!(stats.timeline.len(), 2);
512 }
513
514 #[test]
515 fn test_hot_call_stack() {
516 let hot_stack = HotCallStack {
517 call_stack_hash: 12345,
518 total_frequency: 100,
519 total_size: 8192,
520 impact_score: 819200,
521 threads: vec![1, 2, 3],
522 };
523
524 assert_eq!(hot_stack.call_stack_hash, 12345);
525 assert_eq!(hot_stack.total_frequency, 100);
526 assert_eq!(hot_stack.total_size, 8192);
527 assert_eq!(hot_stack.impact_score, 819200);
528 assert_eq!(hot_stack.threads.len(), 3);
529 }
530
531 #[test]
532 fn test_thread_interaction() {
533 let interaction = ThreadInteraction {
534 thread_a: 1,
535 thread_b: 2,
536 shared_patterns: vec![12345, 67890],
537 interaction_strength: 50,
538 interaction_type: InteractionType::SimilarPatterns,
539 };
540
541 assert_eq!(interaction.thread_a, 1);
542 assert_eq!(interaction.thread_b, 2);
543 assert_eq!(interaction.shared_patterns.len(), 2);
544 assert_eq!(interaction.interaction_strength, 50);
545 }
546
547 #[test]
548 fn test_memory_peak() {
549 let peak = MemoryPeak {
550 timestamp: 123456789,
551 thread_id: 3,
552 memory_usage: 1048576,
553 active_allocations: 500,
554 triggering_call_stack: 12345,
555 };
556
557 assert_eq!(peak.timestamp, 123456789);
558 assert_eq!(peak.thread_id, 3);
559 assert_eq!(peak.memory_usage, 1048576);
560 assert_eq!(peak.active_allocations, 500);
561 assert_eq!(peak.triggering_call_stack, 12345);
562 }
563
564 #[test]
565 fn test_performance_bottleneck_types() {
566 let bottleneck1 = PerformanceBottleneck {
567 bottleneck_type: BottleneckType::HighFrequencySmallAllocation,
568 thread_id: 1,
569 call_stack_hash: 12345,
570 severity: 0.8,
571 description: "High frequency".to_string(),
572 suggestion: "Use pools".to_string(),
573 };
574
575 let bottleneck2 = PerformanceBottleneck {
576 bottleneck_type: BottleneckType::LargeAllocationSpike,
577 thread_id: 2,
578 call_stack_hash: 67890,
579 severity: 0.6,
580 description: "Large spike".to_string(),
581 suggestion: "Optimize allocation".to_string(),
582 };
583
584 assert_eq!(bottleneck1.thread_id, 1);
586 assert_eq!(bottleneck2.thread_id, 2);
587 }
588
589 #[test]
590 fn test_interaction_types() {
591 let similar = InteractionType::SimilarPatterns;
592 let sharing = InteractionType::MemorySharing;
593 let producer_consumer = InteractionType::ProducerConsumer;
594
595 let _interactions = [similar, sharing, producer_consumer];
598 }
599
600 #[test]
601 fn test_bottleneck_types_complete() {
602 let types = [
603 BottleneckType::HighFrequencySmallAllocation,
604 BottleneckType::LargeAllocationSpike,
605 BottleneckType::MemoryLeak,
606 BottleneckType::ThreadContention,
607 BottleneckType::FragmentationRisk,
608 ];
609
610 assert_eq!(types.len(), 5);
612 }
613
614 #[test]
615 fn test_analysis_summary_fields() {
616 let summary = AnalysisSummary {
617 total_threads: 5,
618 total_allocations: 1000,
619 total_deallocations: 950,
620 peak_memory_usage: 16384,
621 total_memory_allocated: 32768,
622 unique_call_stacks: 25,
623 analysis_duration_ms: 500,
624 sampling_effectiveness: 85.5,
625 };
626
627 assert_eq!(summary.total_threads, 5);
628 assert_eq!(summary.total_allocations, 1000);
629 assert_eq!(summary.total_deallocations, 950);
630 assert_eq!(summary.peak_memory_usage, 16384);
631 assert_eq!(summary.total_memory_allocated, 32768);
632 assert_eq!(summary.unique_call_stacks, 25);
633 assert_eq!(summary.analysis_duration_ms, 500);
634 assert_eq!(summary.sampling_effectiveness, 85.5);
635 }
636
637 #[test]
638 fn test_complex_analysis_workflow() {
639 let mut analysis = LockfreeAnalysis::new();
640 let start_time = Instant::now();
641
642 analysis
644 .thread_stats
645 .insert(1, create_test_thread_stats(1, 1000, 8192));
646 analysis
647 .thread_stats
648 .insert(2, create_test_thread_stats(2, 500, 4096));
649 analysis
650 .thread_stats
651 .insert(3, create_test_thread_stats(3, 2000, 16384));
652
653 analysis.hottest_call_stacks.push(HotCallStack {
654 call_stack_hash: 12345,
655 total_frequency: 1000,
656 total_size: 16384,
657 impact_score: 16384000,
658 threads: vec![1, 2, 3],
659 });
660
661 analysis.thread_interactions.push(ThreadInteraction {
662 thread_a: 1,
663 thread_b: 2,
664 shared_patterns: vec![12345],
665 interaction_strength: 75,
666 interaction_type: InteractionType::SimilarPatterns,
667 });
668
669 analysis.memory_peaks.push(MemoryPeak {
670 timestamp: 1000000,
671 thread_id: 3,
672 memory_usage: 16384,
673 active_allocations: 1000,
674 triggering_call_stack: 12345,
675 });
676
677 analysis
678 .performance_bottlenecks
679 .push(PerformanceBottleneck {
680 bottleneck_type: BottleneckType::HighFrequencySmallAllocation,
681 thread_id: 3,
682 call_stack_hash: 12345,
683 severity: 0.9,
684 description: "Very high allocation frequency".to_string(),
685 suggestion: "Consider object pooling".to_string(),
686 });
687
688 analysis.calculate_summary(start_time);
689
690 assert_eq!(analysis.summary.total_threads, 3);
692 assert_eq!(analysis.summary.total_allocations, 3500);
693 assert!(analysis.get_most_active_threads(1)[0].1 == 2000); assert!(analysis.get_highest_memory_threads(1)[0].1 == 16384); assert_eq!(analysis.get_critical_bottlenecks(0.8).len(), 1);
696 assert_eq!(analysis.hottest_call_stacks.len(), 1);
697 assert_eq!(analysis.thread_interactions.len(), 1);
698 assert_eq!(analysis.memory_peaks.len(), 1);
699 }
700
701 #[test]
702 fn test_edge_cases() {
703 let mut analysis = LockfreeAnalysis::new();
704
705 let empty_stats = ThreadStats {
707 thread_id: 99,
708 total_allocations: 0,
709 total_deallocations: 0,
710 peak_memory: 0,
711 total_allocated: 0,
712 allocation_frequency: HashMap::new(),
713 avg_allocation_size: 0.0,
714 timeline: Vec::new(),
715 };
716 analysis.thread_stats.insert(99, empty_stats);
717
718 let start_time = Instant::now();
719 analysis.calculate_summary(start_time);
720
721 assert_eq!(analysis.summary.total_threads, 1);
722 assert_eq!(analysis.summary.total_allocations, 0);
723 assert_eq!(analysis.summary.sampling_effectiveness, 0.0);
724
725 let active = analysis.get_most_active_threads(10);
727 assert_eq!(active.len(), 1);
728 assert_eq!(active[0].1, 0); }
730
731 #[test]
732 fn test_serialization_deserialization() {
733 let analysis = LockfreeAnalysis::new();
735
736 let _json = serde_json::to_string(&analysis);
738
739 let event = AllocationEvent {
740 timestamp: 1000,
741 ptr: 0x1000,
742 size: 1024,
743 call_stack_hash: 12345,
744 event_type: EventType::Allocation,
745 thread_id: 1,
746 };
747
748 let _event_json = serde_json::to_string(&event);
749 }
750}