1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::atomic::{AtomicU64, Ordering};
9
10static EVENT_ID_GENERATOR: AtomicU64 = AtomicU64::new(1);
12
13pub struct OwnershipHistoryRecorder {
15 ownership_events: HashMap<usize, Vec<OwnershipEvent>>,
17 ownership_summaries: HashMap<usize, OwnershipSummary>,
19 config: HistoryConfig,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct HistoryConfig {
26 pub max_events_per_allocation: usize,
28 pub track_borrowing: bool,
30 pub track_cloning: bool,
32 pub track_ownership_transfers: bool,
34}
35
36impl Default for HistoryConfig {
37 fn default() -> Self {
38 Self {
39 max_events_per_allocation: 100,
40 track_borrowing: true,
41 track_cloning: true,
42 track_ownership_transfers: true,
43 }
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
49pub enum OwnershipEventType {
50 Allocated,
52 Cloned { source_ptr: usize },
54 Dropped,
56 OwnershipTransferred { target_var: String },
58 Borrowed { borrower_scope: String },
60 MutablyBorrowed { borrower_scope: String },
62 BorrowReleased { borrower_scope: String },
64 RefCountChanged { old_count: usize, new_count: usize },
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct OwnershipEvent {
71 pub event_id: u64,
73 pub timestamp: u64,
75 pub event_type: OwnershipEventType,
77 pub source_stack_id: u32,
79 pub details: OwnershipEventDetails,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct OwnershipEventDetails {
86 pub clone_source_ptr: Option<usize>,
88 pub transfer_target_var: Option<String>,
90 pub borrower_scope: Option<String>,
92 pub ref_count_info: Option<RefCountInfo>,
94 pub context: Option<String>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct RefCountInfo {
101 pub strong_count: usize,
102 pub weak_count: usize,
103 pub data_ptr: usize,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct OwnershipSummary {
109 pub allocation_ptr: usize,
111 pub lifetime_ms: Option<u64>,
113 pub borrow_info: BorrowInfo,
115 pub clone_info: CloneInfo,
117 pub ownership_history_available: bool,
119 pub total_events: usize,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct BorrowInfo {
126 pub immutable_borrows: u32,
128 pub mutable_borrows: u32,
130 pub max_concurrent_borrows: u32,
132 pub last_borrow_timestamp: Option<u64>,
134 pub active_borrows: Vec<ActiveBorrow>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct ActiveBorrow {
141 pub borrower_scope: String,
142 pub borrow_type: BorrowType,
143 pub start_timestamp: u64,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub enum BorrowType {
149 Immutable,
150 Mutable,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct CloneInfo {
156 pub clone_count: u32,
158 pub is_clone: bool,
160 pub original_ptr: Option<usize>,
162 pub cloned_ptrs: Vec<usize>,
164}
165
166impl OwnershipHistoryRecorder {
167 pub fn new() -> Self {
169 Self::with_config(HistoryConfig::default())
170 }
171
172 pub fn with_config(config: HistoryConfig) -> Self {
174 Self {
175 ownership_events: HashMap::new(),
176 ownership_summaries: HashMap::new(),
177 config,
178 }
179 }
180
181 pub fn record_event(
183 &mut self,
184 ptr: usize,
185 event_type: OwnershipEventType,
186 source_stack_id: u32,
187 ) {
188 let event_id = EVENT_ID_GENERATOR.fetch_add(1, Ordering::Relaxed);
189 let timestamp = self.get_current_timestamp();
190
191 let details = self.create_event_details(&event_type);
192
193 let event = OwnershipEvent {
194 event_id,
195 timestamp,
196 event_type: event_type.clone(),
197 source_stack_id,
198 details,
199 };
200
201 let events = self.ownership_events.entry(ptr).or_default();
203 events.push(event);
204
205 if events.len() > self.config.max_events_per_allocation {
207 events.remove(0); }
209
210 self.update_ownership_summary(ptr, &event_type, timestamp);
212 }
213
214 fn create_event_details(&self, event_type: &OwnershipEventType) -> OwnershipEventDetails {
216 match event_type {
217 OwnershipEventType::Cloned { source_ptr } => OwnershipEventDetails {
218 clone_source_ptr: Some(*source_ptr),
219 transfer_target_var: None,
220 borrower_scope: None,
221 ref_count_info: None,
222 context: Some("Memory cloned from another allocation".to_string()),
223 },
224 OwnershipEventType::OwnershipTransferred { target_var } => OwnershipEventDetails {
225 clone_source_ptr: None,
226 transfer_target_var: Some(target_var.clone()),
227 borrower_scope: None,
228 ref_count_info: None,
229 context: Some("Ownership transferred to another variable".to_string()),
230 },
231 OwnershipEventType::Borrowed { borrower_scope } => OwnershipEventDetails {
232 clone_source_ptr: None,
233 transfer_target_var: None,
234 borrower_scope: Some(borrower_scope.clone()),
235 ref_count_info: None,
236 context: Some("Memory immutably borrowed".to_string()),
237 },
238 OwnershipEventType::MutablyBorrowed { borrower_scope } => OwnershipEventDetails {
239 clone_source_ptr: None,
240 transfer_target_var: None,
241 borrower_scope: Some(borrower_scope.clone()),
242 ref_count_info: None,
243 context: Some("Memory mutably borrowed".to_string()),
244 },
245 OwnershipEventType::BorrowReleased { borrower_scope } => OwnershipEventDetails {
246 clone_source_ptr: None,
247 transfer_target_var: None,
248 borrower_scope: Some(borrower_scope.clone()),
249 ref_count_info: None,
250 context: Some("Borrow released".to_string()),
251 },
252 OwnershipEventType::RefCountChanged {
253 old_count,
254 new_count,
255 } => OwnershipEventDetails {
256 clone_source_ptr: None,
257 transfer_target_var: None,
258 borrower_scope: None,
259 ref_count_info: Some(RefCountInfo {
260 strong_count: *new_count,
261 weak_count: 0, data_ptr: 0, }),
264 context: Some(format!(
265 "Reference count changed from {old_count} to {new_count}",
266 )),
267 },
268 _ => OwnershipEventDetails {
269 clone_source_ptr: None,
270 transfer_target_var: None,
271 borrower_scope: None,
272 ref_count_info: None,
273 context: None,
274 },
275 }
276 }
277
278 fn update_ownership_summary(
280 &mut self,
281 ptr: usize,
282 event_type: &OwnershipEventType,
283 timestamp: u64,
284 ) {
285 let summary = self
286 .ownership_summaries
287 .entry(ptr)
288 .or_insert_with(|| OwnershipSummary {
289 allocation_ptr: ptr,
290 lifetime_ms: None,
291 borrow_info: BorrowInfo {
292 immutable_borrows: 0,
293 mutable_borrows: 0,
294 max_concurrent_borrows: 0,
295 last_borrow_timestamp: None,
296 active_borrows: Vec::new(),
297 },
298 clone_info: CloneInfo {
299 clone_count: 0,
300 is_clone: false,
301 original_ptr: None,
302 cloned_ptrs: Vec::new(),
303 },
304 ownership_history_available: true,
305 total_events: 0,
306 });
307
308 summary.total_events += 1;
309
310 match event_type {
311 OwnershipEventType::Borrowed { borrower_scope } => {
312 summary.borrow_info.immutable_borrows += 1;
313 summary.borrow_info.last_borrow_timestamp = Some(timestamp);
314 summary.borrow_info.active_borrows.push(ActiveBorrow {
315 borrower_scope: borrower_scope.clone(),
316 borrow_type: BorrowType::Immutable,
317 start_timestamp: timestamp,
318 });
319 summary.borrow_info.max_concurrent_borrows = summary
320 .borrow_info
321 .max_concurrent_borrows
322 .max(summary.borrow_info.active_borrows.len() as u32);
323 }
324 OwnershipEventType::MutablyBorrowed { borrower_scope } => {
325 summary.borrow_info.mutable_borrows += 1;
326 summary.borrow_info.last_borrow_timestamp = Some(timestamp);
327 summary.borrow_info.active_borrows.push(ActiveBorrow {
328 borrower_scope: borrower_scope.clone(),
329 borrow_type: BorrowType::Mutable,
330 start_timestamp: timestamp,
331 });
332 summary.borrow_info.max_concurrent_borrows = summary
333 .borrow_info
334 .max_concurrent_borrows
335 .max(summary.borrow_info.active_borrows.len() as u32);
336 }
337 OwnershipEventType::BorrowReleased { borrower_scope } => {
338 summary
340 .borrow_info
341 .active_borrows
342 .retain(|borrow| borrow.borrower_scope != *borrower_scope);
343 }
344 OwnershipEventType::Cloned { source_ptr } => {
345 summary.clone_info.is_clone = true;
346 summary.clone_info.original_ptr = Some(*source_ptr);
347
348 if let Some(source_summary) = self.ownership_summaries.get_mut(source_ptr) {
350 source_summary.clone_info.clone_count += 1;
351 source_summary.clone_info.cloned_ptrs.push(ptr);
352 }
353 }
354 _ => {
355 }
357 }
358 }
359
360 pub fn get_events(&self, ptr: usize) -> Option<&Vec<OwnershipEvent>> {
362 self.ownership_events.get(&ptr)
363 }
364
365 pub fn get_summary(&self, ptr: usize) -> Option<&OwnershipSummary> {
367 self.ownership_summaries.get(&ptr)
368 }
369
370 pub fn get_all_summaries(&self) -> &HashMap<usize, OwnershipSummary> {
372 &self.ownership_summaries
373 }
374
375 pub fn export_to_json(&self) -> serde_json::Result<String> {
377 let export_data = OwnershipHistoryExport {
378 summaries: self.ownership_summaries.clone(),
379 detailed_events: self.ownership_events.clone(),
380 export_timestamp: self.get_current_timestamp(),
381 config: self.config.clone(),
382 };
383
384 serde_json::to_string_pretty(&export_data)
385 }
386
387 pub fn clear(&mut self) {
389 self.ownership_events.clear();
390 self.ownership_summaries.clear();
391 }
392
393 pub fn get_statistics(&self) -> OwnershipStatistics {
395 let total_allocations = self.ownership_summaries.len();
396 let total_events = self
397 .ownership_events
398 .values()
399 .map(|events| events.len())
400 .sum();
401
402 let mut event_type_counts = HashMap::new();
403 for events in self.ownership_events.values() {
404 for event in events {
405 let event_type_name = match &event.event_type {
406 OwnershipEventType::Allocated => "Allocated",
407 OwnershipEventType::Cloned { .. } => "Cloned",
408 OwnershipEventType::Dropped => "Dropped",
409 OwnershipEventType::OwnershipTransferred { .. } => "OwnershipTransferred",
410 OwnershipEventType::Borrowed { .. } => "Borrowed",
411 OwnershipEventType::MutablyBorrowed { .. } => "MutablyBorrowed",
412 OwnershipEventType::BorrowReleased { .. } => "BorrowReleased",
413 OwnershipEventType::RefCountChanged { .. } => "RefCountChanged",
414 };
415 *event_type_counts
416 .entry(event_type_name.to_string())
417 .or_insert(0) += 1;
418 }
419 }
420
421 let cloned_allocations = self
422 .ownership_summaries
423 .values()
424 .filter(|summary| summary.clone_info.is_clone)
425 .count();
426
427 let allocations_with_borrows = self
428 .ownership_summaries
429 .values()
430 .filter(|summary| {
431 summary.borrow_info.immutable_borrows > 0 || summary.borrow_info.mutable_borrows > 0
432 })
433 .count();
434
435 OwnershipStatistics {
436 total_allocations,
437 total_events,
438 event_type_counts,
439 cloned_allocations,
440 allocations_with_borrows,
441 average_events_per_allocation: if total_allocations > 0 {
442 total_events as f64 / total_allocations as f64
443 } else {
444 0.0
445 },
446 }
447 }
448
449 fn get_current_timestamp(&self) -> u64 {
451 std::time::SystemTime::now()
452 .duration_since(std::time::UNIX_EPOCH)
453 .unwrap_or_default()
454 .as_nanos() as u64
455 }
456}
457
458impl Default for OwnershipHistoryRecorder {
459 fn default() -> Self {
460 Self::new()
461 }
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
466pub struct OwnershipHistoryExport {
467 pub summaries: HashMap<usize, OwnershipSummary>,
468 pub detailed_events: HashMap<usize, Vec<OwnershipEvent>>,
469 pub export_timestamp: u64,
470 pub config: HistoryConfig,
471}
472
473#[derive(Debug, Clone, Serialize)]
475pub struct OwnershipStatistics {
476 pub total_allocations: usize,
477 pub total_events: usize,
478 pub event_type_counts: HashMap<String, usize>,
479 pub cloned_allocations: usize,
480 pub allocations_with_borrows: usize,
481 pub average_events_per_allocation: f64,
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487
488 #[test]
489 fn test_ownership_history_recorder_creation() {
490 let recorder = OwnershipHistoryRecorder::new();
491 assert_eq!(recorder.ownership_events.len(), 0);
492 assert_eq!(recorder.ownership_summaries.len(), 0);
493 }
494
495 #[test]
496 fn test_record_allocation_event() {
497 let mut recorder = OwnershipHistoryRecorder::new();
498 let ptr = 0x1000;
499
500 recorder.record_event(ptr, OwnershipEventType::Allocated, 1);
501
502 let events = recorder.get_events(ptr).expect("Failed to get events");
503 assert_eq!(events.len(), 1);
504 assert!(matches!(
505 events[0].event_type,
506 OwnershipEventType::Allocated
507 ));
508
509 let summary = recorder.get_summary(ptr).expect("Failed to get summary");
510 assert_eq!(summary.allocation_ptr, ptr);
511 assert_eq!(summary.total_events, 1);
512 }
513
514 #[test]
515 fn test_record_clone_event() {
516 let mut recorder = OwnershipHistoryRecorder::new();
517 let source_ptr = 0x1000;
518 let clone_ptr = 0x2000;
519
520 recorder.record_event(source_ptr, OwnershipEventType::Allocated, 1);
522
523 recorder.record_event(clone_ptr, OwnershipEventType::Cloned { source_ptr }, 2);
525
526 let clone_summary = recorder
527 .get_summary(clone_ptr)
528 .expect("Failed to get clone summary");
529 assert!(clone_summary.clone_info.is_clone);
530 assert_eq!(clone_summary.clone_info.original_ptr, Some(source_ptr));
531
532 let source_summary = recorder
533 .get_summary(source_ptr)
534 .expect("Failed to get source summary");
535 assert_eq!(source_summary.clone_info.clone_count, 1);
536 assert!(source_summary.clone_info.cloned_ptrs.contains(&clone_ptr));
537 }
538
539 #[test]
540 fn test_record_borrow_events() {
541 let mut recorder = OwnershipHistoryRecorder::new();
542 let ptr = 0x1000;
543
544 recorder.record_event(ptr, OwnershipEventType::Allocated, 1);
545 recorder.record_event(
546 ptr,
547 OwnershipEventType::Borrowed {
548 borrower_scope: "scope1".to_string(),
549 },
550 2,
551 );
552 recorder.record_event(
553 ptr,
554 OwnershipEventType::MutablyBorrowed {
555 borrower_scope: "scope2".to_string(),
556 },
557 3,
558 );
559
560 let summary = recorder.get_summary(ptr).expect("Test operation failed");
561 assert_eq!(summary.borrow_info.immutable_borrows, 1);
562 assert_eq!(summary.borrow_info.mutable_borrows, 1);
563 assert_eq!(summary.borrow_info.max_concurrent_borrows, 2);
564 assert_eq!(summary.borrow_info.active_borrows.len(), 2);
565 }
566
567 #[test]
568 fn test_borrow_release() {
569 let mut recorder = OwnershipHistoryRecorder::new();
570 let ptr = 0x1000;
571
572 recorder.record_event(ptr, OwnershipEventType::Allocated, 1);
573 recorder.record_event(
574 ptr,
575 OwnershipEventType::Borrowed {
576 borrower_scope: "scope1".to_string(),
577 },
578 2,
579 );
580 recorder.record_event(
581 ptr,
582 OwnershipEventType::BorrowReleased {
583 borrower_scope: "scope1".to_string(),
584 },
585 3,
586 );
587
588 let summary = recorder.get_summary(ptr).expect("Test operation failed");
589 assert_eq!(summary.borrow_info.immutable_borrows, 1);
590 assert_eq!(summary.borrow_info.active_borrows.len(), 0);
591 }
592
593 #[test]
594 fn test_event_limit() {
595 let config = HistoryConfig {
596 max_events_per_allocation: 3,
597 ..Default::default()
598 };
599 let mut recorder = OwnershipHistoryRecorder::with_config(config);
600 let ptr = 0x1000;
601
602 for i in 0..5 {
604 recorder.record_event(ptr, OwnershipEventType::Allocated, i as u32);
605 }
606
607 let events = recorder.get_events(ptr).expect("Failed to get events");
608 assert_eq!(events.len(), 3); }
610
611 #[test]
612 fn test_statistics() {
613 let mut recorder = OwnershipHistoryRecorder::new();
614
615 recorder.record_event(0x1000, OwnershipEventType::Allocated, 1);
616 recorder.record_event(0x2000, OwnershipEventType::Cloned { source_ptr: 0x1000 }, 2);
617 recorder.record_event(
618 0x1000,
619 OwnershipEventType::Borrowed {
620 borrower_scope: "scope1".to_string(),
621 },
622 3,
623 );
624
625 let stats = recorder.get_statistics();
626 assert_eq!(stats.total_allocations, 2);
627 assert_eq!(stats.total_events, 3);
628 assert_eq!(stats.cloned_allocations, 1);
629 assert_eq!(stats.allocations_with_borrows, 1);
630 }
631
632 #[test]
633 fn test_json_export() {
634 let mut recorder = OwnershipHistoryRecorder::new();
635 recorder.record_event(0x1000, OwnershipEventType::Allocated, 1);
636
637 let json = recorder.export_to_json().expect("Failed to export to JSON");
638 assert!(json.contains("summaries"));
639 assert!(json.contains("detailed_events"));
640 assert!(json.contains("export_timestamp"));
641 }
642}