1use crate::core::ownership_history::{
7 BorrowInfo, CloneInfo, OwnershipHistoryRecorder, OwnershipSummary,
8};
9use crate::core::types::AllocationInfo;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13pub struct LifecycleSummaryGenerator {
15 config: SummaryConfig,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SummaryConfig {
22 pub include_borrow_details: bool,
24 pub include_clone_details: bool,
26 pub min_lifetime_threshold_ms: u64,
28 pub max_events_per_allocation: usize,
30}
31
32impl Default for SummaryConfig {
33 fn default() -> Self {
34 Self {
35 include_borrow_details: true,
36 include_clone_details: true,
37 min_lifetime_threshold_ms: 0,
38 max_events_per_allocation: 50,
39 }
40 }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct LifecycleExportData {
46 pub lifecycle_events: Vec<LifecycleEventSummary>,
48 pub variable_groups: Vec<VariableGroup>,
50 pub user_variables_count: usize,
52 pub visualization_ready: bool,
54 pub metadata: ExportMetadata,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct LifecycleEventSummary {
61 pub allocation_ptr: usize,
63 pub var_name: Option<String>,
65 pub type_name: Option<String>,
67 pub size: usize,
69 pub lifetime_ms: Option<u64>,
71 pub events: Vec<LifecycleEvent>,
73 pub summary: AllocationLifecycleSummary,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct LifecycleEvent {
80 pub id: u64,
82 pub event_type: String,
84 pub timestamp: u64,
86 pub size: Option<usize>,
88 pub details: Option<String>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct AllocationLifecycleSummary {
95 pub lifetime_ms: Option<u64>,
97 pub borrow_info: BorrowInfo,
99 pub clone_info: CloneInfo,
101 pub ownership_history_available: bool,
103 pub lifecycle_pattern: LifecyclePattern,
105 pub efficiency_score: f64,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub enum LifecyclePattern {
112 Ephemeral,
114 ShortTerm,
116 MediumTerm,
118 LongTerm,
120 Leaked,
122 Unknown,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct VariableGroup {
129 pub name: String,
131 pub variables: Vec<String>,
133 pub total_memory: usize,
135 pub average_lifetime_ms: f64,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ExportMetadata {
142 pub export_timestamp: u64,
144 pub total_allocations: usize,
146 pub total_events: usize,
148 pub analysis_duration_ms: u64,
150}
151
152impl LifecycleSummaryGenerator {
153 pub fn new() -> Self {
155 Self::with_config(SummaryConfig::default())
156 }
157
158 pub fn with_config(config: SummaryConfig) -> Self {
160 Self { config }
161 }
162
163 pub fn generate_lifecycle_export(
165 &self,
166 ownership_history: &OwnershipHistoryRecorder,
167 allocations: &[AllocationInfo],
168 ) -> LifecycleExportData {
169 let start_time = std::time::Instant::now();
170
171 let lifecycle_events = self.generate_lifecycle_events(ownership_history, allocations);
173
174 let variable_groups = self.generate_variable_groups(&lifecycle_events);
176
177 let user_variables_count = lifecycle_events
179 .iter()
180 .filter(|event| {
181 event
182 .var_name
183 .as_ref()
184 .map(|name| self.is_user_variable(name))
185 .unwrap_or(false)
186 })
187 .count();
188
189 let analysis_duration = start_time.elapsed().as_millis() as u64;
190
191 LifecycleExportData {
192 lifecycle_events,
193 variable_groups,
194 user_variables_count,
195 visualization_ready: true,
196 metadata: ExportMetadata {
197 export_timestamp: self.get_current_timestamp(),
198 total_allocations: allocations.len(),
199 total_events: ownership_history.get_statistics().total_events,
200 analysis_duration_ms: analysis_duration,
201 },
202 }
203 }
204
205 fn generate_lifecycle_events(
207 &self,
208 ownership_history: &OwnershipHistoryRecorder,
209 allocations: &[AllocationInfo],
210 ) -> Vec<LifecycleEventSummary> {
211 let mut summaries = Vec::new();
212
213 for allocation in allocations {
214 if let Some(lifetime_ms) = allocation.lifetime_ms {
216 if lifetime_ms < self.config.min_lifetime_threshold_ms {
217 continue;
218 }
219 }
220
221 let summary = self.generate_single_lifecycle_summary(ownership_history, allocation);
222 summaries.push(summary);
223 }
224
225 summaries
226 }
227
228 fn generate_single_lifecycle_summary(
230 &self,
231 ownership_history: &OwnershipHistoryRecorder,
232 allocation: &AllocationInfo,
233 ) -> LifecycleEventSummary {
234 let ptr = allocation.ptr;
235
236 let ownership_summary = ownership_history.get_summary(ptr);
238
239 let events = if let Some(ownership_events) = ownership_history.get_events(ptr) {
241 ownership_events
242 .iter()
243 .take(self.config.max_events_per_allocation)
244 .map(|event| LifecycleEvent {
245 id: event.event_id,
246 event_type: self.format_event_type(&event.event_type),
247 timestamp: event.timestamp,
248 size: Some(allocation.size),
249 details: event.details.context.clone(),
250 })
251 .collect()
252 } else {
253 let mut basic_events = vec![LifecycleEvent {
255 id: 1,
256 event_type: "Allocation".to_string(),
257 timestamp: allocation.timestamp_alloc,
258 size: Some(allocation.size),
259 details: Some("Memory allocated".to_string()),
260 }];
261
262 if let Some(dealloc_time) = allocation.timestamp_dealloc {
263 basic_events.push(LifecycleEvent {
264 id: 2,
265 event_type: "Deallocation".to_string(),
266 timestamp: dealloc_time,
267 size: Some(allocation.size),
268 details: Some("Memory deallocated".to_string()),
269 });
270 }
271
272 basic_events
273 };
274
275 let summary = if let Some(ownership_summary) = ownership_summary {
277 AllocationLifecycleSummary {
278 lifetime_ms: allocation.lifetime_ms,
279 borrow_info: ownership_summary.borrow_info.clone(),
280 clone_info: ownership_summary.clone_info.clone(),
281 ownership_history_available: true,
282 lifecycle_pattern: self.classify_lifecycle_pattern(allocation.lifetime_ms),
283 efficiency_score: self.calculate_efficiency_score(allocation, ownership_summary),
284 }
285 } else {
286 AllocationLifecycleSummary {
287 lifetime_ms: allocation.lifetime_ms,
288 borrow_info: BorrowInfo {
289 immutable_borrows: 0,
290 mutable_borrows: 0,
291 max_concurrent_borrows: 0,
292 last_borrow_timestamp: None,
293 active_borrows: Vec::new(),
294 },
295 clone_info: CloneInfo {
296 clone_count: 0,
297 is_clone: false,
298 original_ptr: None,
299 cloned_ptrs: Vec::new(),
300 },
301 ownership_history_available: false,
302 lifecycle_pattern: self.classify_lifecycle_pattern(allocation.lifetime_ms),
303 efficiency_score: 0.5, }
305 };
306
307 LifecycleEventSummary {
308 allocation_ptr: ptr,
309 var_name: allocation.var_name.clone(),
310 type_name: allocation.type_name.clone(),
311 size: allocation.size,
312 lifetime_ms: allocation.lifetime_ms,
313 events,
314 summary,
315 }
316 }
317
318 fn format_event_type(
320 &self,
321 event_type: &crate::core::ownership_history::OwnershipEventType,
322 ) -> String {
323 match event_type {
324 crate::core::ownership_history::OwnershipEventType::Allocated => {
325 "Allocation".to_string()
326 }
327 crate::core::ownership_history::OwnershipEventType::Cloned { .. } => {
328 "Clone".to_string()
329 }
330 crate::core::ownership_history::OwnershipEventType::Dropped => {
331 "Deallocation".to_string()
332 }
333 crate::core::ownership_history::OwnershipEventType::OwnershipTransferred { .. } => {
334 "OwnershipTransfer".to_string()
335 }
336 crate::core::ownership_history::OwnershipEventType::Borrowed { .. } => {
337 "Borrow".to_string()
338 }
339 crate::core::ownership_history::OwnershipEventType::MutablyBorrowed { .. } => {
340 "MutableBorrow".to_string()
341 }
342 crate::core::ownership_history::OwnershipEventType::BorrowReleased { .. } => {
343 "BorrowRelease".to_string()
344 }
345 crate::core::ownership_history::OwnershipEventType::RefCountChanged { .. } => {
346 "RefCountChange".to_string()
347 }
348 }
349 }
350
351 fn classify_lifecycle_pattern(&self, lifetime_ms: Option<u64>) -> LifecyclePattern {
353 match lifetime_ms {
354 None => LifecyclePattern::Leaked,
355 Some(0) => LifecyclePattern::Ephemeral,
356 Some(ms) if ms < 1 => LifecyclePattern::Ephemeral,
357 Some(ms) if ms < 100 => LifecyclePattern::ShortTerm,
358 Some(ms) if ms < 10_000 => LifecyclePattern::MediumTerm,
359 Some(_) => LifecyclePattern::LongTerm,
360 }
361 }
362
363 fn calculate_efficiency_score(
365 &self,
366 allocation: &AllocationInfo,
367 ownership_summary: &OwnershipSummary,
368 ) -> f64 {
369 let mut score: f64 = 0.5; if allocation
373 .var_name
374 .as_ref()
375 .map(|name| self.is_user_variable(name))
376 .unwrap_or(false)
377 {
378 score += 0.1;
379 }
380
381 match self.classify_lifecycle_pattern(allocation.lifetime_ms) {
383 LifecyclePattern::ShortTerm | LifecyclePattern::MediumTerm => score += 0.2,
384 LifecyclePattern::Ephemeral => score -= 0.1,
385 LifecyclePattern::Leaked => score -= 0.3,
386 _ => {}
387 }
388
389 if ownership_summary.borrow_info.max_concurrent_borrows > 5 {
391 score -= 0.1;
392 }
393
394 if ownership_summary.clone_info.clone_count > 0 || ownership_summary.clone_info.is_clone {
396 score += 0.1;
397 }
398
399 score.clamp(0.0, 1.0)
401 }
402
403 fn is_user_variable(&self, name: &str) -> bool {
405 !name.starts_with("primitive_")
407 && !name.starts_with("struct_")
408 && !name.starts_with("collection_")
409 && !name.starts_with("buffer_")
410 && !name.starts_with("system_")
411 && !name.starts_with("fast_tracked")
412 && name != "unknown"
413 }
414
415 fn generate_variable_groups(
417 &self,
418 lifecycle_events: &[LifecycleEventSummary],
419 ) -> Vec<VariableGroup> {
420 let mut groups: HashMap<String, Vec<&LifecycleEventSummary>> = HashMap::new();
421
422 for event in lifecycle_events {
424 if let Some(ref type_name) = event.type_name {
425 let group_name = self.extract_base_type_name(type_name);
426 groups.entry(group_name).or_default().push(event);
427 }
428 }
429
430 groups
432 .into_iter()
433 .map(|(name, events)| {
434 let variables: Vec<String> =
435 events.iter().filter_map(|e| e.var_name.clone()).collect();
436
437 let total_memory: usize = events.iter().map(|e| e.size).sum();
438
439 let average_lifetime_ms = if !events.is_empty() {
440 let total_lifetime: u64 = events.iter().filter_map(|e| e.lifetime_ms).sum();
441 let count = events.iter().filter(|e| e.lifetime_ms.is_some()).count();
442 if count > 0 {
443 total_lifetime as f64 / count as f64
444 } else {
445 0.0
446 }
447 } else {
448 0.0
449 };
450
451 VariableGroup {
452 name,
453 variables,
454 total_memory,
455 average_lifetime_ms,
456 }
457 })
458 .collect()
459 }
460
461 fn extract_base_type_name(&self, type_name: &str) -> String {
463 if let Some(pos) = type_name.find('<') {
465 type_name[..pos].to_string()
466 } else if let Some(pos) = type_name.rfind("::") {
467 type_name[pos + 2..].to_string()
468 } else {
469 type_name.to_string()
470 }
471 }
472
473 fn get_current_timestamp(&self) -> u64 {
475 std::time::SystemTime::now()
476 .duration_since(std::time::UNIX_EPOCH)
477 .unwrap_or_default()
478 .as_nanos() as u64
479 }
480
481 pub fn export_to_json(&self, export_data: &LifecycleExportData) -> serde_json::Result<String> {
483 serde_json::to_string_pretty(export_data)
484 }
485}
486
487impl Default for LifecycleSummaryGenerator {
488 fn default() -> Self {
489 Self::new()
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use crate::core::ownership_history::{OwnershipEventType, OwnershipHistoryRecorder};
497 use crate::core::types::AllocationInfo;
498
499 fn create_test_allocation(ptr: usize, size: usize, var_name: Option<String>) -> AllocationInfo {
500 AllocationInfo {
501 ptr,
502 size,
503 var_name,
504 type_name: Some("String".to_string()),
505 scope_name: Some("test_scope".to_string()),
506 timestamp_alloc: 1000000,
507 timestamp_dealloc: Some(2000000),
508 thread_id: "test_thread".to_string(),
509 borrow_count: 0,
510 stack_trace: Some(vec!["test_function".to_string()]),
511 is_leaked: false,
512 lifetime_ms: Some(1000),
513 borrow_info: None,
514 clone_info: None,
515 ownership_history_available: false,
516 smart_pointer_info: None,
517 memory_layout: None,
518 generic_info: None,
519 dynamic_type_info: None,
520 runtime_state: None,
521 stack_allocation: None,
522 temporary_object: None,
523 fragmentation_analysis: None,
524 generic_instantiation: None,
525 type_relationships: None,
526 type_usage: None,
527 function_call_tracking: None,
528 lifecycle_tracking: None,
529 access_tracking: None,
530 drop_chain_analysis: None,
531 }
532 }
533
534 #[test]
535 fn test_json_export() {
536 let generator = LifecycleSummaryGenerator::default();
537 let export_data = LifecycleExportData {
538 lifecycle_events: vec![],
539 variable_groups: vec![],
540 user_variables_count: 0,
541 visualization_ready: true,
542 metadata: ExportMetadata {
543 export_timestamp: 0,
544 total_allocations: 0,
545 total_events: 0,
546 analysis_duration_ms: 0,
547 },
548 };
549
550 let json = generator.export_to_json(&export_data).unwrap();
551 assert!(json.contains("lifecycle_events"));
552 assert!(json.contains("variable_groups"));
553 }
554
555 #[test]
556 fn test_generate_single_lifecycle_summary() {
557 let generator = LifecycleSummaryGenerator::default();
558 let mut history = OwnershipHistoryRecorder::default();
559
560 let ptr = 0x1000;
562 let size = 1024;
563 let alloc = create_test_allocation(ptr, size, Some("test_var".to_string()));
564
565 history.record_event(ptr, OwnershipEventType::Allocated, 1);
567
568 let summary = generator.generate_single_lifecycle_summary(&history, &alloc);
569
570 assert_eq!(summary.allocation_ptr, ptr);
571 assert_eq!(summary.size, size);
572 assert_eq!(summary.var_name, Some("test_var".to_string()));
573 assert_eq!(summary.type_name, Some("String".to_string()));
574 }
575
576 #[test]
577 fn test_calculate_efficiency_score() {
578 let generator = LifecycleSummaryGenerator::default();
579 let alloc = create_test_allocation(0x1000, 1024, Some("test_var".to_string()));
580 let mut history = OwnershipHistoryRecorder::default();
581
582 history.record_event(0x1000, OwnershipEventType::Allocated, 1);
584
585 let summary = history.get_summary(0x1000).unwrap();
586 let score = generator.calculate_efficiency_score(&alloc, summary);
587 assert!((0.0..=1.0).contains(&score));
588 }
589
590 #[test]
591 fn test_generate_lifecycle_events() {
592 let generator = LifecycleSummaryGenerator::default();
593 let history = OwnershipHistoryRecorder::default();
594
595 let alloc1 = create_test_allocation(0x1000, 1024, Some("var1".to_string()));
597 let alloc2 = create_test_allocation(0x2000, 2048, Some("var2".to_string()));
598
599 let events = generator.generate_lifecycle_events(&history, &[alloc1, alloc2]);
600 assert_eq!(events.len(), 2);
601 }
602
603 #[test]
604 fn test_format_event_type() {
605 let generator = LifecycleSummaryGenerator::default();
606
607 let allocated = generator.format_event_type(&OwnershipEventType::Allocated);
608 assert_eq!(allocated, "Allocation");
609
610 let borrowed = generator.format_event_type(&OwnershipEventType::Borrowed {
611 borrower_scope: "scope1".to_string(),
612 });
613 assert_eq!(borrowed, "Borrow");
614
615 let cloned =
616 generator.format_event_type(&OwnershipEventType::Cloned { source_ptr: 0x1000 });
617 assert_eq!(cloned, "Clone");
618 }
619
620 #[test]
621 fn test_generate_lifecycle_export() {
622 let generator = LifecycleSummaryGenerator::default();
623 let history = OwnershipHistoryRecorder::default();
624 let alloc = create_test_allocation(0x1000, 1024, Some("test_var".to_string()));
625
626 let export = generator.generate_lifecycle_export(&history, &[alloc]);
627
628 assert_eq!(export.lifecycle_events.len(), 1);
629 assert_eq!(export.user_variables_count, 1);
630 assert!(export.visualization_ready);
631 }
632
633 #[test]
634 fn test_extract_base_type_name() {
635 let generator = LifecycleSummaryGenerator::default();
636
637 assert_eq!(generator.extract_base_type_name("String"), "String");
638 assert_eq!(
639 generator.extract_base_type_name("std::string::String"),
640 "String"
641 );
642 assert_eq!(generator.extract_base_type_name("Vec<u8>"), "Vec");
643 assert_eq!(
644 generator.extract_base_type_name("std::vec::Vec<u8>"),
645 "std::vec::Vec"
646 );
647 assert_eq!(generator.extract_base_type_name("&str"), "&str");
648 }
649
650 #[test]
651 fn test_is_user_variable() {
652 let generator = LifecycleSummaryGenerator::default();
653
654 assert!(generator.is_user_variable("user_var"));
655 assert!(generator.is_user_variable("my_var_123"));
656 assert!(!generator.is_user_variable("primitive_var"));
657 assert!(!generator.is_user_variable("struct_var"));
658 assert!(!generator.is_user_variable("unknown"));
659 }
660
661 #[test]
662 fn test_lifecycle_pattern_classification() {
663 let generator = LifecycleSummaryGenerator::default();
664
665 assert!(matches!(
666 generator.classify_lifecycle_pattern(None),
667 LifecyclePattern::Leaked
668 ));
669 assert!(matches!(
670 generator.classify_lifecycle_pattern(Some(0)),
671 LifecyclePattern::Ephemeral
672 ));
673 assert!(matches!(
674 generator.classify_lifecycle_pattern(Some(50)),
675 LifecyclePattern::ShortTerm
676 ));
677 assert!(matches!(
678 generator.classify_lifecycle_pattern(Some(5000)),
679 LifecyclePattern::MediumTerm
680 ));
681 assert!(matches!(
682 generator.classify_lifecycle_pattern(Some(15000)),
683 LifecyclePattern::LongTerm
684 ));
685 }
686
687 #[test]
688 fn test_variable_groups_generation() {
689 let generator = LifecycleSummaryGenerator::default();
690
691 let events = vec![
692 LifecycleEventSummary {
693 allocation_ptr: 0x1000,
694 var_name: Some("str1".to_string()),
695 type_name: Some("String".to_string()),
696 size: 100,
697 lifetime_ms: Some(1000),
698 events: vec![],
699 summary: AllocationLifecycleSummary {
700 lifetime_ms: Some(1000),
701 borrow_info: BorrowInfo {
702 immutable_borrows: 0,
703 mutable_borrows: 0,
704 max_concurrent_borrows: 0,
705 last_borrow_timestamp: None,
706 active_borrows: vec![],
707 },
708 clone_info: CloneInfo {
709 clone_count: 0,
710 is_clone: false,
711 original_ptr: None,
712 cloned_ptrs: vec![],
713 },
714 ownership_history_available: false,
715 lifecycle_pattern: LifecyclePattern::ShortTerm,
716 efficiency_score: 0.5,
717 },
718 },
719 LifecycleEventSummary {
720 allocation_ptr: 0x2000,
721 var_name: Some("str2".to_string()),
722 type_name: Some("String".to_string()),
723 size: 200,
724 lifetime_ms: Some(2000),
725 events: vec![],
726 summary: AllocationLifecycleSummary {
727 lifetime_ms: Some(2000),
728 borrow_info: BorrowInfo {
729 immutable_borrows: 0,
730 mutable_borrows: 0,
731 max_concurrent_borrows: 0,
732 last_borrow_timestamp: None,
733 active_borrows: vec![],
734 },
735 clone_info: CloneInfo {
736 clone_count: 0,
737 is_clone: false,
738 original_ptr: None,
739 cloned_ptrs: vec![],
740 },
741 ownership_history_available: false,
742 lifecycle_pattern: LifecyclePattern::MediumTerm,
743 efficiency_score: 0.5,
744 },
745 },
746 ];
747
748 let groups = generator.generate_variable_groups(&events);
749 assert_eq!(groups.len(), 1); assert_eq!(groups[0].name, "String");
751 assert_eq!(groups[0].total_memory, 300);
752 assert_eq!(groups[0].variables.len(), 2);
753 }
754}