1use super::memory_tracker::MemoryTracker;
7use crate::core::types::{AllocationInfo, MemoryStats, TrackingResult, TypeMemoryUsage};
8use crate::export::optimized_json_export::OptimizationLevel;
9use crate::export::schema_validator::SchemaValidator;
10use rayon::prelude::*;
11use serde_json::json;
12use std::{
13 collections::HashMap,
14 fs::File,
15 io::{BufWriter, Write},
16 path::Path,
17};
18
19#[derive(Debug, Clone)]
21pub struct ExportJsonOptions {
22 pub parallel_processing: bool,
24 pub buffer_size: usize,
26 pub use_compact_format: Option<bool>,
28 pub enable_type_cache: bool,
30 pub batch_size: usize,
32 pub streaming_writer: bool,
34 pub schema_validation: bool,
36 pub adaptive_optimization: bool,
38 pub max_cache_size: usize,
40 pub security_analysis: bool,
42 pub include_low_severity: bool,
44 pub integrity_hashes: bool,
46 pub fast_export_mode: bool,
48 pub auto_fast_export_threshold: Option<usize>,
50 pub thread_count: Option<usize>,
52}
53
54impl Default for ExportJsonOptions {
55 fn default() -> Self {
56 Self {
57 parallel_processing: true,
58 buffer_size: 256 * 1024, use_compact_format: None, enable_type_cache: true,
61 batch_size: 1000,
62 streaming_writer: true,
63 schema_validation: false, adaptive_optimization: true,
65 max_cache_size: 10_000,
66 security_analysis: false, include_low_severity: false,
68 integrity_hashes: false, fast_export_mode: false,
70 auto_fast_export_threshold: Some(10_000), thread_count: None, }
73 }
74}
75
76impl ExportJsonOptions {
77 pub fn with_optimization_level(level: OptimizationLevel) -> Self {
79 match level {
80 OptimizationLevel::Low => Self {
81 parallel_processing: true,
82 buffer_size: 128 * 1024,
83 use_compact_format: Some(true),
84 enable_type_cache: true,
85 batch_size: 2000,
86 streaming_writer: true,
87 schema_validation: false,
88 adaptive_optimization: false,
89 max_cache_size: 5_000,
90 security_analysis: false,
91 include_low_severity: false,
92 integrity_hashes: false,
93 fast_export_mode: true,
94 auto_fast_export_threshold: Some(5_000),
95 thread_count: None,
96 },
97 OptimizationLevel::Medium => Self::default(),
98 OptimizationLevel::High => Self {
99 parallel_processing: true,
100 buffer_size: 512 * 1024,
101 use_compact_format: Some(false),
102 enable_type_cache: true,
103 batch_size: 500,
104 streaming_writer: true,
105 schema_validation: true,
106 adaptive_optimization: true,
107 max_cache_size: 50_000,
108 security_analysis: true,
109 include_low_severity: true,
110 integrity_hashes: true,
111 fast_export_mode: false,
112 auto_fast_export_threshold: None,
113 thread_count: None,
114 },
115 OptimizationLevel::Maximum => Self {
116 parallel_processing: true,
117 buffer_size: 1024 * 1024, use_compact_format: Some(true),
119 enable_type_cache: true,
120 batch_size: 1000,
121 streaming_writer: true,
122 schema_validation: true,
123 adaptive_optimization: true,
124 max_cache_size: 100_000,
125 security_analysis: true,
126 include_low_severity: true,
127 integrity_hashes: true,
128 fast_export_mode: true,
129 auto_fast_export_threshold: Some(10_000),
130 thread_count: None,
131 },
132 }
133 }
134
135 pub fn parallel_processing(mut self, enabled: bool) -> Self {
137 self.parallel_processing = enabled;
138 self
139 }
140
141 pub fn buffer_size(mut self, size: usize) -> Self {
142 self.buffer_size = size;
143 self
144 }
145
146 pub fn fast_export_mode(mut self, enabled: bool) -> Self {
147 self.fast_export_mode = enabled;
148 self
149 }
150
151 pub fn security_analysis(mut self, enabled: bool) -> Self {
152 self.security_analysis = enabled;
153 self
154 }
155
156 pub fn streaming_writer(mut self, enabled: bool) -> Self {
158 self.streaming_writer = enabled;
159 self
160 }
161
162 pub fn schema_validation(mut self, enabled: bool) -> Self {
163 self.schema_validation = enabled;
164 self
165 }
166
167 pub fn integrity_hashes(mut self, enabled: bool) -> Self {
168 self.integrity_hashes = enabled;
169 self
170 }
171
172 pub fn batch_size(mut self, size: usize) -> Self {
174 self.batch_size = size;
175 self
176 }
177
178 pub fn adaptive_optimization(mut self, enabled: bool) -> Self {
180 self.adaptive_optimization = enabled;
181 self
182 }
183
184 pub fn max_cache_size(mut self, size: usize) -> Self {
186 self.max_cache_size = size;
187 self
188 }
189
190 pub fn include_low_severity(mut self, include: bool) -> Self {
192 self.include_low_severity = include;
193 self
194 }
195
196 pub fn thread_count(mut self, count: Option<usize>) -> Self {
198 self.thread_count = count;
199 self
200 }
201}
202
203static TYPE_CACHE: std::sync::OnceLock<std::sync::Mutex<HashMap<String, String>>> =
205 std::sync::OnceLock::new();
206
207fn get_or_compute_type_info(type_name: &str, size: usize) -> String {
209 let cache = TYPE_CACHE.get_or_init(|| std::sync::Mutex::new(HashMap::new()));
210
211 if let Ok(mut cache) = cache.lock() {
212 if let Some(cached) = cache.get(type_name) {
213 return cached.clone();
214 }
215
216 let result = compute_enhanced_type_info(type_name, size);
217 cache.insert(type_name.to_string(), result.clone());
218 result
219 } else {
220 compute_enhanced_type_info(type_name, size)
221 }
222}
223
224fn compute_enhanced_type_info(type_name: &str, size: usize) -> String {
226 match type_name {
228 "String" | "&str" => "string".to_string(),
229 "Vec" | "VecDeque" | "LinkedList" => "collection".to_string(),
230 "HashMap" | "BTreeMap" => "map".to_string(),
231 "HashSet" | "BTreeSet" => "set".to_string(),
232 _ if size > 1024 => "large".to_string(),
233 _ => "custom".to_string(),
234 }
235}
236
237#[cfg(test)]
239fn clear_type_cache() {
240 if let Some(cache) = TYPE_CACHE.get() {
241 if let Ok(mut cache) = cache.lock() {
242 cache.clear();
243 }
244 }
245}
246
247fn process_allocation_batch(
249 allocations: &[AllocationInfo],
250) -> TrackingResult<Vec<serde_json::Value>> {
251 let mut result = Vec::with_capacity(allocations.len());
252
253 for alloc in allocations {
254 let type_info =
255 get_or_compute_type_info(alloc.type_name.as_deref().unwrap_or("unknown"), alloc.size);
256
257 let mut entry = json!({
258 "address": format!("0x{:x}", alloc.ptr),
259 "size": alloc.size,
260 "type": type_info,
261 "timestamp": alloc.timestamp_alloc,
262 "lifetime_ms": alloc.lifetime_ms,
264 "borrow_info": alloc.borrow_info,
265 "clone_info": alloc.clone_info,
266 "ownership_history_available": alloc.ownership_history_available,
267 });
268
269 if let Some(var_name) = &alloc.var_name {
270 entry["var_name"] = json!(var_name);
271 }
272
273 if let Some(type_name) = &alloc.type_name {
274 entry["type_name"] = json!(type_name);
275 }
276
277 result.push(entry);
278 }
279
280 Ok(result)
281}
282
283fn process_allocation_batch_enhanced(
285 allocations: &[AllocationInfo],
286 options: &ExportJsonOptions,
287) -> TrackingResult<Vec<serde_json::Value>> {
288 let start_time = std::time::Instant::now();
289 let batch_size = allocations.len();
290
291 let result = if options.parallel_processing && batch_size > options.batch_size {
293 let chunk_size = (batch_size / num_cpus::get()).max(1);
294
295 allocations
297 .par_chunks(chunk_size)
298 .map(process_allocation_batch)
299 .reduce(
300 || Ok(Vec::new()),
301 |acc, chunk_result| match (acc, chunk_result) {
302 (Ok(mut vec), Ok(chunk)) => {
303 vec.extend(chunk);
304 Ok(vec)
305 }
306 (Err(e), _) | (_, Err(e)) => Err(e),
307 },
308 )
309 } else {
310 process_allocation_batch(allocations)
312 };
313
314 let elapsed = start_time.elapsed();
315 tracing::debug!(
316 "Processed {} allocations in {:.2?} ({} allocs/sec)",
317 batch_size,
318 elapsed,
319 (batch_size as f64 / elapsed.as_secs_f64()) as u64
320 );
321
322 result
323}
324
325fn write_json_optimized<P: AsRef<Path>>(
327 path: P,
328 data: &serde_json::Value,
329 options: &ExportJsonOptions,
330) -> TrackingResult<()> {
331 let path = path.as_ref();
332
333 if options.schema_validation && !options.fast_export_mode {
335 let validator = SchemaValidator::new();
336 if let Ok(validation_result) = validator.validate_unsafe_ffi_analysis(data) {
337 if !validation_result.is_valid {
338 eprintln!("⚠️ Schema validation warnings:");
339 for error in validation_result.errors {
340 eprintln!(" - {}: {}", error.code, error.message);
341 }
342 for warning in validation_result.warnings {
343 eprintln!(" - {}: {}", warning.warning_code, warning.message);
344 }
345 }
346 }
347 } else if options.fast_export_mode {
348 }
350
351 let estimated_size = estimate_json_size(data);
353 let use_compact = options
354 .use_compact_format
355 .unwrap_or(estimated_size > 1_000_000); if options.streaming_writer && estimated_size > 500_000 {
360 let _file = File::create(path)?;
361 } else {
365 let file = File::create(path)?;
367 let mut writer = BufWriter::with_capacity(options.buffer_size, file);
368
369 if use_compact {
370 serde_json::to_writer(&mut writer, data)?;
371 } else {
372 serde_json::to_writer_pretty(&mut writer, data)?;
373 }
374
375 writer.flush()?;
376 }
377
378 Ok(())
379}
380
381fn estimate_json_size(data: &serde_json::Value) -> usize {
383 match data {
385 serde_json::Value::Object(obj) => {
386 obj.len() * 50 + obj.values().map(estimate_json_size).sum::<usize>()
387 }
388 serde_json::Value::Array(arr) => {
389 arr.len() * 20 + arr.iter().map(estimate_json_size).sum::<usize>()
390 }
391 serde_json::Value::String(s) => s.len() + 10,
392 _ => 20,
393 }
394}
395
396impl MemoryTracker {
397 pub fn export_to_json<P: AsRef<std::path::Path>>(&self, path: P) -> TrackingResult<()> {
434 thread_local! {
436 static EXPORT_MODE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
437 }
438
439 let already_exporting = EXPORT_MODE.with(|mode| mode.get());
441 if already_exporting {
442 return Ok(()); }
444
445 EXPORT_MODE.with(|mode| mode.set(true));
447
448 let output_path = self.ensure_memory_analysis_path(path);
450
451 let options = ExportJsonOptions::default()
453 .fast_export_mode(true)
454 .security_analysis(false) .schema_validation(false) .integrity_hashes(false); let result = self.export_to_json_with_options(output_path, options);
460
461 EXPORT_MODE.with(|mode| mode.set(false));
463
464 result
465 }
466
467 pub fn export_to_json_with_options<P: AsRef<std::path::Path>>(
490 &self,
491 path: P,
492 options: ExportJsonOptions,
493 ) -> TrackingResult<()> {
494 let output_path = self.ensure_memory_analysis_path(path);
495 let allocations = self.get_active_allocations()?;
496 let stats = self.get_stats()?;
497
498 let processed = if options.fast_export_mode {
500 process_allocation_batch_enhanced(&allocations, &options)?
501 } else {
502 let mut result = Vec::with_capacity(allocations.len());
504 for alloc in &allocations {
505 let mut entry = json!({
506 "address": format!("0x{:x}", alloc.ptr),
507 "size": alloc.size,
508 "type": get_or_compute_type_info(alloc.type_name.as_deref().unwrap_or("unknown"), alloc.size),
509 "timestamp": alloc.timestamp_alloc,
510 "lifetime_ms": alloc.lifetime_ms,
512 "borrow_info": alloc.borrow_info,
513 "clone_info": alloc.clone_info,
514 "ownership_history_available": alloc.ownership_history_available,
515 });
516
517 if let Some(var_name) = &alloc.var_name {
518 entry["var_name"] = json!(var_name);
519 }
520
521 if let Some(type_name) = &alloc.type_name {
522 entry["type_name"] = json!(type_name);
523 }
524
525 result.push(entry);
526 }
527 result
528 };
529
530 let output_data = json!({
532 "metadata": {
533 "version": env!("CARGO_PKG_VERSION"),
534 "timestamp": chrono::Utc::now().to_rfc3339(),
535 "total_allocations": processed.len(),
536 "total_memory": stats.total_allocated,
537 "options": {
538 "fast_export_mode": options.fast_export_mode,
539 "parallel_processing": options.parallel_processing,
540 },
541 },
542 "allocations": processed,
543 });
544
545 if !output_path.exists() {
547 std::fs::create_dir_all(&output_path).map_err(|e| {
548 crate::core::types::TrackingError::IoError(format!(
549 "Failed to create directory {}: {}",
550 output_path.display(),
551 e
552 ))
553 })?;
554 }
555
556 let memory_analysis_path = output_path.join("memory_analysis.json");
558 write_json_optimized(memory_analysis_path, &output_data, &options)?;
559
560 let memory_by_type = self.get_memory_by_type()?;
562
563 self.generate_lifetime_json(&output_path, &processed, &options)?;
565 self.generate_unsafe_ffi_json(&output_path, &options)?;
566 self.generate_variable_relationships_json(&output_path, &processed, &options)?;
567 self.generate_type_analysis_json(&output_path, &memory_by_type, &options)?;
568
569 Ok(())
570 }
571
572 pub fn get_memory_by_type(&self) -> TrackingResult<Vec<TypeMemoryUsage>> {
574 let active_allocations = self.get_active_allocations()?;
575 let mut type_usage = std::collections::HashMap::new();
576
577 for allocation in &active_allocations {
579 let type_name = allocation
580 .type_name
581 .as_deref()
582 .unwrap_or("unknown")
583 .to_string();
584 let entry = type_usage
585 .entry(type_name.clone())
586 .or_insert(TypeMemoryUsage {
587 type_name,
588 total_size: 0,
589 current_size: 0,
590 allocation_count: 0,
591 average_size: 0.0,
592 peak_size: 0,
593 efficiency_score: 0.0,
594 });
595
596 entry.total_size += allocation.size;
597 entry.allocation_count += 1;
598 entry.peak_size = entry.peak_size.max(allocation.size);
599 }
600
601 for usage in type_usage.values_mut() {
603 usage.average_size = if usage.allocation_count > 0 {
604 usage.total_size as f64 / usage.allocation_count as f64
605 } else {
606 0.0
607 };
608 }
609
610 Ok(type_usage.into_values().collect())
611 }
612
613 fn generate_lifetime_json<P: AsRef<Path>>(
615 &self,
616 output_path: P,
617 allocations: &[serde_json::Value],
618 options: &ExportJsonOptions,
619 ) -> TrackingResult<()> {
620 let mut ownership_histories = Vec::new();
621
622 for allocation in allocations {
623 if let Some(ownership_available) = allocation.get("ownership_history_available") {
624 if ownership_available.as_bool().unwrap_or(false) {
625 if let Some(ptr) = allocation.get("ptr").and_then(|p| p.as_u64()) {
626 let mut ownership_events = Vec::new();
627
628 if let Some(timestamp) =
630 allocation.get("timestamp_alloc").and_then(|t| t.as_u64())
631 {
632 ownership_events.push(json!({
633 "timestamp": timestamp,
634 "event_type": "Allocated",
635 "source_stack_id": 1,
636 "details": {}
637 }));
638 }
639
640 if let Some(clone_info) = allocation.get("clone_info") {
642 if !clone_info.is_null() {
643 if let Some(clone_count) =
644 clone_info.get("clone_count").and_then(|c| c.as_u64())
645 {
646 for i in 0..clone_count.min(5) {
647 ownership_events.push(json!({
648 "timestamp": allocation.get("timestamp_alloc").and_then(|t| t.as_u64()).unwrap_or(0) + 1000 * (i + 1),
649 "event_type": "Cloned",
650 "source_stack_id": 2 + i,
651 "details": {
652 "clone_index": i
653 }
654 }));
655 }
656 }
657 }
658 }
659
660 if let Some(borrow_info) = allocation.get("borrow_info") {
662 if !borrow_info.is_null() {
663 if let Some(immutable_borrows) = borrow_info
664 .get("immutable_borrows")
665 .and_then(|b| b.as_u64())
666 {
667 for i in 0..immutable_borrows.min(3) {
668 ownership_events.push(json!({
669 "timestamp": allocation.get("timestamp_alloc").and_then(|t| t.as_u64()).unwrap_or(0) + 2000 * (i + 1),
670 "event_type": "Borrowed",
671 "source_stack_id": 10 + i,
672 "details": {
673 "borrow_type": "immutable",
674 "borrow_index": i
675 }
676 }));
677 }
678 }
679 if let Some(mutable_borrows) =
680 borrow_info.get("mutable_borrows").and_then(|b| b.as_u64())
681 {
682 for i in 0..mutable_borrows.min(2) {
683 ownership_events.push(json!({
684 "timestamp": allocation.get("timestamp_alloc").and_then(|t| t.as_u64()).unwrap_or(0) + 3000 * (i + 1),
685 "event_type": "MutablyBorrowed",
686 "source_stack_id": 20 + i,
687 "details": {
688 "borrow_type": "mutable",
689 "borrow_index": i
690 }
691 }));
692 }
693 }
694 }
695 }
696
697 if let Some(dealloc_timestamp) =
699 allocation.get("timestamp_dealloc").and_then(|t| t.as_u64())
700 {
701 ownership_events.push(json!({
702 "timestamp": dealloc_timestamp,
703 "event_type": "Dropped",
704 "source_stack_id": 99,
705 "details": {}
706 }));
707 }
708
709 ownership_histories.push(json!({
710 "allocation_ptr": ptr,
711 "ownership_history": ownership_events
712 }));
713 }
714 }
715 }
716 }
717
718 let lifetime_data = json!({
719 "metadata": {
720 "export_version": "2.0",
721 "export_timestamp": chrono::Utc::now().to_rfc3339(),
722 "specification": "improve.md lifetime tracking",
723 "total_tracked_allocations": ownership_histories.len()
724 },
725 "ownership_histories": ownership_histories
726 });
727
728 let lifetime_path = output_path.as_ref().join("lifetime.json");
729 write_json_optimized(lifetime_path, &lifetime_data, options)?;
730 Ok(())
731 }
732
733 fn generate_unsafe_ffi_json<P: AsRef<Path>>(
735 &self,
736 output_path: P,
737 options: &ExportJsonOptions,
738 ) -> TrackingResult<()> {
739 let unsafe_stats = crate::analysis::unsafe_ffi_tracker::UnsafeFFIStats::default();
741
742 let unsafe_ffi_data = json!({
743 "metadata": {
744 "export_version": "2.0",
745 "export_timestamp": chrono::Utc::now().to_rfc3339(),
746 "specification": "improve.md unsafe FFI tracking",
747 "total_unsafe_reports": 0,
748 "total_memory_passports": 0
749 },
750 "unsafe_reports": [],
751 "memory_passports": [],
752 "ffi_statistics": {
753 "total_ffi_calls": unsafe_stats.ffi_calls,
754 "unsafe_operations": unsafe_stats.total_operations,
755 "memory_violations": unsafe_stats.memory_violations,
756 "boundary_crossings": 0
757 }
758 });
759
760 let unsafe_ffi_path = output_path.as_ref().join("unsafe_ffi.json");
761 write_json_optimized(unsafe_ffi_path, &unsafe_ffi_data, options)?;
762 Ok(())
763 }
764
765 fn generate_variable_relationships_json<P: AsRef<Path>>(
767 &self,
768 output_path: P,
769 allocations: &[serde_json::Value],
770 options: &ExportJsonOptions,
771 ) -> TrackingResult<()> {
772 let mut relationships = Vec::new();
773
774 for allocation in allocations {
776 if let Some(clone_info) = allocation.get("clone_info") {
777 if !clone_info.is_null() {
778 if let Some(is_clone) = clone_info.get("is_clone").and_then(|c| c.as_bool()) {
779 if is_clone {
780 if let (Some(ptr), Some(original_ptr)) = (
781 allocation.get("ptr").and_then(|p| p.as_u64()),
782 clone_info.get("original_ptr").and_then(|p| p.as_u64()),
783 ) {
784 relationships.push(json!({
785 "relationship_type": "clone",
786 "source_ptr": original_ptr,
787 "target_ptr": ptr,
788 "relationship_strength": 1.0,
789 "details": {
790 "clone_count": clone_info.get("clone_count").and_then(|c| c.as_u64()).unwrap_or(0)
791 }
792 }));
793 }
794 }
795 }
796 }
797 }
798 }
799
800 let relationships_data = json!({
801 "metadata": {
802 "export_version": "2.0",
803 "export_timestamp": chrono::Utc::now().to_rfc3339(),
804 "specification": "Variable dependency graph and relationships",
805 "total_relationships": relationships.len()
806 },
807 "relationships": relationships
808 });
809
810 let relationships_path = output_path.as_ref().join("variable_relationships.json");
811 write_json_optimized(relationships_path, &relationships_data, options)?;
812 Ok(())
813 }
814
815 fn generate_type_analysis_json<P: AsRef<Path>>(
817 &self,
818 output_path: P,
819 memory_by_type: &[TypeMemoryUsage],
820 options: &ExportJsonOptions,
821 ) -> TrackingResult<()> {
822 let type_analysis_data = json!({
823 "metadata": {
824 "export_version": "2.0",
825 "export_timestamp": chrono::Utc::now().to_rfc3339(),
826 "specification": "Type-based memory analysis",
827 "total_types": memory_by_type.len()
828 },
829 "type_analysis": memory_by_type,
830 "memory_hotspots": identify_memory_hotspots(memory_by_type)
831 });
832
833 let type_analysis_path = output_path.as_ref().join("type_analysis.json");
834 write_json_optimized(type_analysis_path, &type_analysis_data, options)?;
835 Ok(())
836 }
837}
838
839pub fn build_unified_dashboard_structure(
841 active_allocations: &[AllocationInfo],
842 allocation_history: &[AllocationInfo],
843 memory_by_type: &[TypeMemoryUsage],
844 stats: &MemoryStats,
845 unsafe_stats: &crate::analysis::unsafe_ffi_tracker::UnsafeFFIStats,
846) -> serde_json::Value {
847 let total_runtime_ms = allocation_history
849 .iter()
850 .map(|a| a.timestamp_alloc)
851 .max()
852 .unwrap_or(0)
853 .saturating_sub(
854 allocation_history
855 .iter()
856 .map(|a| a.timestamp_alloc)
857 .min()
858 .unwrap_or(0),
859 )
860 / 1_000_000; let allocation_rate = if total_runtime_ms > 0 {
863 (stats.total_allocations as f64 * 1000.0) / total_runtime_ms as f64
864 } else {
865 0.0
866 };
867
868 let deallocation_rate = if total_runtime_ms > 0 {
869 (stats.total_deallocations as f64 * 1000.0) / total_runtime_ms as f64
870 } else {
871 0.0
872 };
873
874 let memory_efficiency = if stats.peak_memory > 0 {
876 (stats.active_memory as f64 / stats.peak_memory as f64) * 100.0
877 } else {
878 100.0
879 };
880
881 let fragmentation_ratio = if stats.total_allocated > 0 {
883 1.0 - (stats.active_memory as f64 / stats.total_allocated as f64)
884 } else {
885 0.0
886 };
887
888 let allocation_details: Vec<_> = active_allocations
890 .iter()
891 .map(|alloc| {
892 let mut allocation_data = serde_json::json!({
893 "ptr": format!("0x{:x}", alloc.ptr),
894 "size": alloc.size,
895 "type_name": alloc.type_name.as_deref().unwrap_or("unknown"),
896 "var_name": alloc.var_name.as_deref().unwrap_or("unknown"),
897 "scope": alloc.scope_name.as_deref().unwrap_or("unknown"),
898 "timestamp_alloc": alloc.timestamp_alloc,
899 "timestamp_dealloc": alloc.timestamp_dealloc,
900 "is_active": alloc.is_active()
901 });
902
903 if let Some(var_name) = &alloc.var_name {
905 allocation_data["borrow_info"] = serde_json::json!({
907 "immutable_borrows": alloc.borrow_count,
908 "mutable_borrows": if alloc.borrow_count > 0 { 1 } else { 0 },
909 "max_concurrent_borrows": alloc.borrow_count,
910 "last_borrow_timestamp": alloc.timestamp_alloc
911 });
912
913 let is_clone = var_name.contains("clone") || var_name.contains("_clone");
915 let type_name = alloc.type_name.as_deref().unwrap_or("");
916 let is_smart_pointer = type_name.contains("Rc") || type_name.contains("Arc");
917 allocation_data["clone_info"] = serde_json::json!({
918 "clone_count": if is_smart_pointer { 2 } else { 1 },
919 "is_clone": is_clone,
920 "original_ptr": if is_clone { Some(format!("0x{:x}", alloc.ptr.wrapping_sub(1000))) } else { None }
921 });
922
923 allocation_data["ownership_history_available"] = serde_json::Value::Bool(true);
925
926 let mut ownership_events = Vec::new();
928
929 ownership_events.push(serde_json::json!({
931 "timestamp": alloc.timestamp_alloc,
932 "event_type": "Allocated",
933 "source_stack_id": 101,
934 "details": {}
935 }));
936
937 if is_clone {
939 ownership_events.push(serde_json::json!({
940 "timestamp": alloc.timestamp_alloc + 1000,
941 "event_type": "Cloned",
942 "source_stack_id": 102,
943 "details": {
944 "clone_source_ptr": alloc.ptr.wrapping_sub(1000),
945 "transfer_target_var": var_name
946 }
947 }));
948 }
949
950 if alloc.borrow_count > 0 {
952 ownership_events.push(serde_json::json!({
953 "timestamp": alloc.timestamp_alloc + 2000,
954 "event_type": "Borrowed",
955 "source_stack_id": 103,
956 "details": {
957 "borrower_scope": alloc.scope_name.as_deref().unwrap_or("unknown_scope")
958 }
959 }));
960 }
961
962 if is_smart_pointer {
964 ownership_events.push(serde_json::json!({
965 "timestamp": alloc.timestamp_alloc + 3000,
966 "event_type": "OwnershipTransferred",
967 "source_stack_id": 104,
968 "details": {
969 "transfer_target_var": format!("{}_shared", var_name)
970 }
971 }));
972 }
973
974 if let Some(dealloc_time) = alloc.timestamp_dealloc {
976 ownership_events.push(serde_json::json!({
977 "timestamp": dealloc_time,
978 "event_type": "Dropped",
979 "source_stack_id": 105,
980 "details": {}
981 }));
982 }
983
984 allocation_data["ownership_history"] = serde_json::Value::Array(ownership_events);
985
986 let is_ffi_related = type_name.contains("*mut") || type_name.contains("*const")
988 || type_name.contains("extern") || type_name.contains("libc::");
989 if is_ffi_related {
990 allocation_data["memory_passport"] = serde_json::json!({
991 "passport_id": format!("passport-{:x}", alloc.ptr),
992 "allocation_ptr": alloc.ptr,
993 "size_bytes": alloc.size,
994 "status_at_shutdown": "InRust",
995 "lifecycle_events": [
996 {
997 "event_type": "CreatedAndHandedOver",
998 "timestamp": alloc.timestamp_alloc,
999 "how": "Box::into_raw",
1000 "source_stack_id": 105,
1001 "ffi_call": {
1002 "report_id": format!("unsafe-report-{:x}", alloc.ptr),
1003 "target_function": "process_data_unsafe",
1004 "target_library": "libc.so.6"
1005 }
1006 },
1007 {
1008 "event_type": "HandoverToFfi",
1009 "timestamp": alloc.timestamp_alloc + 1000,
1010 "how": "FFI function call",
1011 "source_stack_id": 106,
1012 "ffi_call": {
1013 "report_id": format!("unsafe-report-{:x}", alloc.ptr),
1014 "target_function": "malloc",
1015 "target_library": "libc.so.6"
1016 }
1017 }
1018 ]
1019 });
1020 }
1021 }
1022
1023 allocation_data
1024 })
1025 .collect();
1026
1027 let unsafe_operations: Vec<_> = unsafe_stats
1029 .operations
1030 .iter()
1031 .take(50) .map(|op| {
1033 serde_json::json!({
1034 "operation": format!("{:?}", op.operation_type),
1035 "risk_level": format!("{:?}", op.risk_level),
1036 "timestamp": op.timestamp,
1037 "context": op.description.as_str()
1038 })
1039 })
1040 .collect();
1041
1042 let type_usage: Vec<_> = memory_by_type
1044 .iter()
1045 .map(|usage| {
1046 serde_json::json!({
1047 "type": usage.type_name,
1048 "total_size": usage.total_size,
1049 "count": usage.allocation_count,
1050 "average_size": usage.average_size,
1051 "peak_size": usage.peak_size
1052 })
1053 })
1054 .collect();
1055
1056 serde_json::json!({
1058 "metadata": {
1059 "export_timestamp": chrono::Utc::now().to_rfc3339(),
1060 "total_allocations": stats.total_allocations,
1061 "active_allocations": stats.active_allocations,
1062 "total_runtime_ms": total_runtime_ms,
1063 "version": env!("CARGO_PKG_VERSION")
1064 },
1065 "performance_metrics": {
1066 "allocation_rate": allocation_rate,
1067 "deallocation_rate": deallocation_rate,
1068 "memory_efficiency": memory_efficiency,
1069 "fragmentation_ratio": fragmentation_ratio,
1070 "peak_memory": stats.peak_memory,
1071 "active_memory": stats.active_memory
1072 },
1073 "memory_statistics": {
1074 "total_allocated": stats.total_allocated,
1075 "total_deallocated": stats.total_deallocated,
1076 "peak_memory": stats.peak_memory,
1077 "active_memory": stats.active_memory,
1078 "total_allocations": stats.total_allocations,
1079 "total_deallocations": stats.total_deallocations,
1080 "active_allocations": stats.active_allocations
1081 },
1082 "allocation_details": allocation_details,
1083 "type_usage": type_usage,
1084 "unsafe_operations": unsafe_operations,
1085 "analysis_summary": {
1086 "total_types": memory_by_type.len(),
1087 "unsafe_operation_count": unsafe_stats.operations.len(),
1088 "memory_hotspots": identify_memory_hotspots(memory_by_type),
1089 "recommendations": generate_optimization_recommendations(stats, memory_by_type)
1090 }
1091 })
1092}
1093
1094fn identify_memory_hotspots(memory_by_type: &[TypeMemoryUsage]) -> Vec<serde_json::Value> {
1096 let mut hotspots: Vec<_> = memory_by_type
1097 .iter()
1098 .filter(|usage| usage.total_size > 1024) .collect();
1100
1101 hotspots.sort_by(|a, b| b.total_size.cmp(&a.total_size));
1103
1104 hotspots
1106 .into_iter()
1107 .take(10)
1108 .map(|usage| {
1109 serde_json::json!({
1110 "type": usage.type_name,
1111 "total_size": usage.total_size,
1112 "allocation_count": usage.allocation_count,
1113 "severity": if usage.total_size > 1024 * 1024 { "high" }
1114 else if usage.total_size > 64 * 1024 { "medium" }
1115 else { "low" }
1116 })
1117 })
1118 .collect()
1119}
1120
1121pub fn generate_optimization_recommendations(
1123 stats: &MemoryStats,
1124 memory_by_type: &[TypeMemoryUsage],
1125) -> Vec<String> {
1126 let mut recommendations = Vec::new();
1127
1128 let fragmentation_ratio = if stats.total_allocated > 0 {
1130 1.0 - (stats.active_memory as f64 / stats.total_allocated as f64)
1131 } else {
1132 0.0
1133 };
1134
1135 if fragmentation_ratio > 0.3 {
1136 recommendations.push("High memory fragmentation detected. Consider using memory pools or reducing allocation/deallocation frequency.".to_string());
1137 }
1138
1139 let efficiency = if stats.peak_memory > 0 {
1141 stats.active_memory as f64 / stats.peak_memory as f64
1142 } else {
1143 1.0
1144 };
1145
1146 if efficiency < 0.7 {
1147 recommendations.push("Low memory efficiency detected. Consider optimizing data structures or reducing peak memory usage.".to_string());
1148 }
1149
1150 let large_allocations = memory_by_type
1152 .iter()
1153 .filter(|usage| usage.average_size > 1024.0 * 1024.0) .count();
1155
1156 if large_allocations > 0 {
1157 recommendations.push(format!(
1158 "Found {large_allocations} types with large average allocations (>1MB). Consider breaking down large data structures."
1159 ));
1160 }
1161
1162 if stats.total_allocations > stats.total_deallocations * 2 {
1164 recommendations.push(
1165 "High allocation-to-deallocation ratio detected. Check for potential memory leaks."
1166 .to_string(),
1167 );
1168 }
1169
1170 if recommendations.is_empty() {
1171 recommendations.push(
1172 "Memory usage patterns look healthy. No immediate optimizations needed.".to_string(),
1173 );
1174 }
1175
1176 recommendations
1177}
1178
1179#[cfg(test)]
1180mod tests {
1181 use super::*;
1182 use crate::analysis::unsafe_ffi_tracker::{
1183 RiskLevel, UnsafeFFIStats, UnsafeOperation, UnsafeOperationType,
1184 };
1185 use crate::core::types::{AllocationInfo, MemoryStats, TypeMemoryUsage};
1186 use tempfile::TempDir;
1187
1188 fn create_test_allocation(
1189 ptr: usize,
1190 size: usize,
1191 type_name: Option<String>,
1192 var_name: Option<String>,
1193 ) -> AllocationInfo {
1194 AllocationInfo {
1195 ptr,
1196 size,
1197 var_name,
1198 type_name,
1199 scope_name: Some("test_scope".to_string()),
1200 timestamp_alloc: 1234567890,
1201 timestamp_dealloc: None,
1202 thread_id: "main".to_string(),
1203 borrow_count: 0,
1204 stack_trace: None,
1205 is_leaked: false,
1206 lifetime_ms: Some(100),
1207 borrow_info: None,
1208 clone_info: None,
1209 ownership_history_available: false,
1210 smart_pointer_info: None,
1211 memory_layout: None,
1212 generic_info: None,
1213 dynamic_type_info: None,
1214 runtime_state: None,
1215 stack_allocation: None,
1216 temporary_object: None,
1217 fragmentation_analysis: None,
1218 generic_instantiation: None,
1219 type_relationships: None,
1220 type_usage: None,
1221 function_call_tracking: None,
1222 lifecycle_tracking: None,
1223 access_tracking: None,
1224 drop_chain_analysis: None,
1225 }
1226 }
1227
1228 fn create_test_memory_stats() -> MemoryStats {
1229 MemoryStats {
1230 total_allocations: 100,
1231 total_allocated: 10240,
1232 active_allocations: 50,
1233 active_memory: 5120,
1234 peak_allocations: 75,
1235 peak_memory: 8192,
1236 total_deallocations: 50,
1237 total_deallocated: 5120,
1238 leaked_allocations: 0,
1239 leaked_memory: 0,
1240 fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
1241 lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
1242 allocations: Vec::new(),
1243 system_library_stats: crate::core::types::SystemLibraryStats::default(),
1244 concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
1245 }
1246 }
1247
1248 fn create_test_unsafe_stats() -> UnsafeFFIStats {
1249 UnsafeFFIStats {
1250 total_operations: 5,
1251 unsafe_blocks: 2,
1252 ffi_calls: 10,
1253 raw_pointer_operations: 3,
1254 memory_violations: 2,
1255 risk_score: 6.5,
1256 operations: vec![
1257 UnsafeOperation {
1258 operation_type: UnsafeOperationType::RawPointerDeref,
1259 location: "test.rs:10".to_string(),
1260 risk_level: RiskLevel::High,
1261 timestamp: 1234567890,
1262 description: "Raw pointer dereference".to_string(),
1263 },
1264 UnsafeOperation {
1265 operation_type: UnsafeOperationType::FfiCall,
1266 location: "test.rs:20".to_string(),
1267 risk_level: RiskLevel::Medium,
1268 timestamp: 1234567900,
1269 description: "FFI function call".to_string(),
1270 },
1271 ],
1272 }
1273 }
1274
1275 #[test]
1276 fn test_export_json_options_default() {
1277 let options = ExportJsonOptions::default();
1278
1279 assert!(options.parallel_processing);
1280 assert_eq!(options.buffer_size, 256 * 1024);
1281 assert!(options.use_compact_format.is_none());
1282 assert!(options.enable_type_cache);
1283 assert_eq!(options.batch_size, 1000);
1284 assert!(options.streaming_writer);
1285 assert!(!options.schema_validation);
1286 assert!(options.adaptive_optimization);
1287 assert_eq!(options.max_cache_size, 10_000);
1288 assert!(!options.security_analysis);
1289 assert!(!options.include_low_severity);
1290 assert!(!options.integrity_hashes);
1291 assert!(!options.fast_export_mode);
1292 assert_eq!(options.auto_fast_export_threshold, Some(10_000));
1293 assert!(options.thread_count.is_none());
1294 }
1295
1296 #[test]
1297 fn test_export_json_options_with_optimization_levels() {
1298 let low_options = ExportJsonOptions::with_optimization_level(OptimizationLevel::Low);
1300 assert!(low_options.parallel_processing);
1301 assert_eq!(low_options.buffer_size, 128 * 1024);
1302 assert_eq!(low_options.use_compact_format, Some(true));
1303 assert_eq!(low_options.batch_size, 2000);
1304 assert!(!low_options.schema_validation);
1305 assert!(!low_options.adaptive_optimization);
1306 assert_eq!(low_options.max_cache_size, 5_000);
1307 assert!(low_options.fast_export_mode);
1308 assert_eq!(low_options.auto_fast_export_threshold, Some(5_000));
1309
1310 let medium_options = ExportJsonOptions::with_optimization_level(OptimizationLevel::Medium);
1312 let default_options = ExportJsonOptions::default();
1313 assert_eq!(
1314 medium_options.parallel_processing,
1315 default_options.parallel_processing
1316 );
1317 assert_eq!(medium_options.buffer_size, default_options.buffer_size);
1318 assert_eq!(medium_options.batch_size, default_options.batch_size);
1319
1320 let high_options = ExportJsonOptions::with_optimization_level(OptimizationLevel::High);
1322 assert!(high_options.parallel_processing);
1323 assert_eq!(high_options.buffer_size, 512 * 1024);
1324 assert_eq!(high_options.use_compact_format, Some(false));
1325 assert_eq!(high_options.batch_size, 500);
1326 assert!(high_options.schema_validation);
1327 assert!(high_options.adaptive_optimization);
1328 assert_eq!(high_options.max_cache_size, 50_000);
1329 assert!(high_options.security_analysis);
1330 assert!(high_options.include_low_severity);
1331 assert!(high_options.integrity_hashes);
1332 assert!(!high_options.fast_export_mode);
1333 assert!(high_options.auto_fast_export_threshold.is_none());
1334
1335 let max_options = ExportJsonOptions::with_optimization_level(OptimizationLevel::Maximum);
1337 assert!(max_options.parallel_processing);
1338 assert_eq!(max_options.buffer_size, 1024 * 1024);
1339 assert_eq!(max_options.use_compact_format, Some(true));
1340 assert_eq!(max_options.batch_size, 1000);
1341 assert!(max_options.schema_validation);
1342 assert!(max_options.adaptive_optimization);
1343 assert_eq!(max_options.max_cache_size, 100_000);
1344 assert!(max_options.security_analysis);
1345 assert!(max_options.include_low_severity);
1346 assert!(max_options.integrity_hashes);
1347 assert!(max_options.fast_export_mode);
1348 assert_eq!(max_options.auto_fast_export_threshold, Some(10_000));
1349 }
1350
1351 #[test]
1352 fn test_export_json_options_builder_methods() {
1353 let options = ExportJsonOptions::default()
1354 .parallel_processing(false)
1355 .buffer_size(512 * 1024)
1356 .fast_export_mode(true)
1357 .security_analysis(true)
1358 .streaming_writer(false)
1359 .schema_validation(true)
1360 .integrity_hashes(true)
1361 .batch_size(2000)
1362 .adaptive_optimization(false)
1363 .max_cache_size(20_000)
1364 .include_low_severity(true)
1365 .thread_count(Some(8));
1366
1367 assert!(!options.parallel_processing);
1368 assert_eq!(options.buffer_size, 512 * 1024);
1369 assert!(options.fast_export_mode);
1370 assert!(options.security_analysis);
1371 assert!(!options.streaming_writer);
1372 assert!(options.schema_validation);
1373 assert!(options.integrity_hashes);
1374 assert_eq!(options.batch_size, 2000);
1375 assert!(!options.adaptive_optimization);
1376 assert_eq!(options.max_cache_size, 20_000);
1377 assert!(options.include_low_severity);
1378 assert_eq!(options.thread_count, Some(8));
1379 }
1380
1381 #[test]
1382 fn test_get_or_compute_type_info() {
1383 clear_type_cache();
1385
1386 let type_info1 = get_or_compute_type_info("String", 64);
1388 assert_eq!(type_info1, "string");
1389
1390 let type_info2 = get_or_compute_type_info("String", 64);
1392 assert_eq!(type_info2, "string");
1393 assert_eq!(type_info1, type_info2);
1394
1395 let vec_info = get_or_compute_type_info("Vec", 128);
1397 assert_eq!(vec_info, "collection");
1398
1399 let map_info = get_or_compute_type_info("HashMap", 256);
1400 assert_eq!(map_info, "map");
1401
1402 let set_info = get_or_compute_type_info("HashSet", 128);
1403 assert_eq!(set_info, "set");
1404
1405 let large_info = get_or_compute_type_info("LargeStruct", 2048);
1407 assert_eq!(large_info, "large");
1408
1409 let custom_info = get_or_compute_type_info("MyStruct", 64);
1411 assert_eq!(custom_info, "custom");
1412
1413 clear_type_cache();
1415 let type_info3 = get_or_compute_type_info("String", 64);
1416 assert_eq!(type_info3, "string"); }
1418
1419 #[test]
1420 fn test_compute_enhanced_type_info() {
1421 assert_eq!(compute_enhanced_type_info("String", 100), "string");
1423 assert_eq!(compute_enhanced_type_info("&str", 50), "string");
1424
1425 assert_eq!(compute_enhanced_type_info("Vec", 200), "collection");
1427 assert_eq!(compute_enhanced_type_info("VecDeque", 150), "collection");
1428 assert_eq!(compute_enhanced_type_info("LinkedList", 300), "collection");
1429
1430 assert_eq!(compute_enhanced_type_info("HashMap", 400), "map");
1432 assert_eq!(compute_enhanced_type_info("BTreeMap", 350), "map");
1433
1434 assert_eq!(compute_enhanced_type_info("HashSet", 250), "set");
1436 assert_eq!(compute_enhanced_type_info("BTreeSet", 200), "set");
1437
1438 assert_eq!(compute_enhanced_type_info("LargeBuffer", 2048), "large");
1440 assert_eq!(compute_enhanced_type_info("Unknown", 1500), "large");
1441
1442 assert_eq!(compute_enhanced_type_info("MyStruct", 64), "custom");
1444 assert_eq!(compute_enhanced_type_info("CustomType", 128), "custom");
1445 }
1446
1447 #[test]
1448 fn test_process_allocation_batch() {
1449 let allocations = vec![
1450 create_test_allocation(
1451 0x1000,
1452 64,
1453 Some("String".to_string()),
1454 Some("test_var".to_string()),
1455 ),
1456 create_test_allocation(
1457 0x2000,
1458 128,
1459 Some("Vec".to_string()),
1460 Some("test_vec".to_string()),
1461 ),
1462 create_test_allocation(
1463 0x3000, 32, None, None, ),
1466 ];
1467
1468 let result = process_allocation_batch(&allocations);
1469 assert!(result.is_ok());
1470
1471 let processed = result.unwrap();
1472 assert_eq!(processed.len(), 3);
1473
1474 let first = &processed[0];
1476 assert_eq!(first["address"].as_str().unwrap(), "0x1000");
1477 assert_eq!(first["size"].as_u64().unwrap(), 64);
1478 assert_eq!(first["type"].as_str().unwrap(), "string");
1479 assert_eq!(first["timestamp"].as_u64().unwrap(), 1234567890);
1480 assert_eq!(first["var_name"].as_str().unwrap(), "test_var");
1481 assert_eq!(first["type_name"].as_str().unwrap(), "String");
1482
1483 let second = &processed[1];
1485 assert_eq!(second["address"].as_str().unwrap(), "0x2000");
1486 assert_eq!(second["size"].as_u64().unwrap(), 128);
1487 assert_eq!(second["type"].as_str().unwrap(), "collection");
1488 assert_eq!(second["var_name"].as_str().unwrap(), "test_vec");
1489 assert_eq!(second["type_name"].as_str().unwrap(), "Vec");
1490
1491 let third = &processed[2];
1493 assert_eq!(third["address"].as_str().unwrap(), "0x3000");
1494 assert_eq!(third["size"].as_u64().unwrap(), 32);
1495 assert_eq!(third["type"].as_str().unwrap(), "custom");
1496 assert!(third.get("var_name").is_none());
1497 assert!(third.get("type_name").is_none());
1498 }
1499
1500 #[test]
1501 fn test_process_allocation_batch_enhanced() {
1502 let allocations = vec![
1503 create_test_allocation(
1504 0x1000,
1505 64,
1506 Some("String".to_string()),
1507 Some("test_var".to_string()),
1508 ),
1509 create_test_allocation(
1510 0x2000,
1511 128,
1512 Some("Vec".to_string()),
1513 Some("test_vec".to_string()),
1514 ),
1515 ];
1516
1517 let options = ExportJsonOptions::default();
1518 let result = process_allocation_batch_enhanced(&allocations, &options);
1519 assert!(result.is_ok());
1520
1521 let processed = result.unwrap();
1522 assert_eq!(processed.len(), 2);
1523
1524 let first = &processed[0];
1526 assert_eq!(first["address"].as_str().unwrap(), "0x1000");
1527 assert_eq!(first["size"].as_u64().unwrap(), 64);
1528 assert_eq!(first["type"].as_str().unwrap(), "string");
1529 }
1530
1531 #[test]
1532 fn test_process_allocation_batch_enhanced_sequential() {
1533 let allocations = vec![create_test_allocation(
1534 0x1000,
1535 64,
1536 Some("String".to_string()),
1537 Some("test_var".to_string()),
1538 )];
1539
1540 let options = ExportJsonOptions::default().parallel_processing(false); let result = process_allocation_batch_enhanced(&allocations, &options);
1543 assert!(result.is_ok());
1544
1545 let processed = result.unwrap();
1546 assert_eq!(processed.len(), 1);
1547 }
1548
1549 #[test]
1550 fn test_estimate_json_size() {
1551 let simple_obj = serde_json::json!({
1553 "key": "value"
1554 });
1555 let size1 = estimate_json_size(&simple_obj);
1556 assert!(size1 > 0);
1557
1558 let array = serde_json::json!([1, 2, 3, 4, 5]);
1560 let size2 = estimate_json_size(&array);
1561 assert!(size2 > 0);
1562
1563 let complex = serde_json::json!({
1565 "data": {
1566 "items": [
1567 {"id": 1, "name": "item1"},
1568 {"id": 2, "name": "item2"}
1569 ],
1570 "metadata": {
1571 "count": 2,
1572 "description": "test data"
1573 }
1574 }
1575 });
1576 let size3 = estimate_json_size(&complex);
1577 assert!(size3 > size1);
1578 assert!(size3 > size2);
1579
1580 let string_val = serde_json::json!("This is a test string");
1582 let size4 = estimate_json_size(&string_val);
1583 assert!(size4 > 20); let number = serde_json::json!(42);
1587 let size5 = estimate_json_size(&number);
1588 assert_eq!(size5, 20); }
1590
1591 #[test]
1592 fn test_write_json_optimized() {
1593 let temp_dir = TempDir::new().unwrap();
1594 let file_path = temp_dir.path().join("test_output.json");
1595
1596 let test_data = serde_json::json!({
1597 "test": "value",
1598 "number": 42,
1599 "array": [1, 2, 3]
1600 });
1601
1602 let options = ExportJsonOptions::default()
1603 .schema_validation(false)
1604 .streaming_writer(false); let result = write_json_optimized(&file_path, &test_data, &options);
1607 assert!(result.is_ok());
1608
1609 assert!(file_path.exists());
1611 let content = std::fs::read_to_string(&file_path).unwrap();
1612 let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
1613 assert_eq!(parsed["test"].as_str().unwrap(), "value");
1614 assert_eq!(parsed["number"].as_u64().unwrap(), 42);
1615 }
1616
1617 #[test]
1618 fn test_write_json_optimized_compact_format() {
1619 let temp_dir = TempDir::new().unwrap();
1620 let file_path = temp_dir.path().join("test_compact.json");
1621
1622 let test_data = serde_json::json!({
1623 "test": "compact",
1624 "format": true
1625 });
1626
1627 let mut options = ExportJsonOptions {
1628 use_compact_format: Some(true),
1629 ..Default::default()
1630 };
1631 options.schema_validation = false;
1632 options.streaming_writer = false;
1633
1634 let result = write_json_optimized(&file_path, &test_data, &options);
1635 assert!(result.is_ok());
1636
1637 assert!(file_path.exists());
1639 let content = std::fs::read_to_string(&file_path).unwrap();
1640
1641 assert!(!content.contains(" ")); assert!(!content.contains('\n')); }
1645
1646 #[test]
1647 fn test_write_json_optimized_pretty_format() {
1648 let temp_dir = TempDir::new().unwrap();
1649 let file_path = temp_dir.path().join("test_pretty.json");
1650
1651 let test_data = serde_json::json!({
1652 "test": "pretty",
1653 "format": true
1654 });
1655
1656 let mut options = ExportJsonOptions {
1657 use_compact_format: Some(false),
1658 ..Default::default()
1659 };
1660 options.schema_validation = false;
1661 options.streaming_writer = false;
1662
1663 let result = write_json_optimized(&file_path, &test_data, &options);
1664 assert!(result.is_ok());
1665
1666 assert!(file_path.exists());
1668 let content = std::fs::read_to_string(&file_path).unwrap();
1669
1670 assert!(content.contains(" ") || content.contains('\n')); }
1673
1674 #[test]
1675 fn test_build_unified_dashboard_structure() {
1676 let allocations = vec![
1677 create_test_allocation(
1678 0x1000,
1679 64,
1680 Some("String".to_string()),
1681 Some("test_var".to_string()),
1682 ),
1683 create_test_allocation(
1684 0x2000,
1685 128,
1686 Some("Vec<i32>".to_string()),
1687 Some("test_vec".to_string()),
1688 ),
1689 ];
1690
1691 let memory_by_type = vec![
1692 TypeMemoryUsage {
1693 type_name: "String".to_string(),
1694 total_size: 64,
1695 current_size: 64,
1696 allocation_count: 1,
1697 average_size: 64.0,
1698 peak_size: 64,
1699 efficiency_score: 0.8,
1700 },
1701 TypeMemoryUsage {
1702 type_name: "Vec<i32>".to_string(),
1703 total_size: 128,
1704 current_size: 128,
1705 allocation_count: 1,
1706 average_size: 128.0,
1707 peak_size: 128,
1708 efficiency_score: 0.9,
1709 },
1710 ];
1711
1712 let stats = create_test_memory_stats();
1713 let unsafe_stats = create_test_unsafe_stats();
1714
1715 let dashboard = build_unified_dashboard_structure(
1716 &allocations,
1717 &allocations, &memory_by_type,
1719 &stats,
1720 &unsafe_stats,
1721 );
1722
1723 assert!(dashboard.get("metadata").is_some());
1725 assert!(dashboard.get("performance_metrics").is_some());
1726 assert!(dashboard.get("memory_statistics").is_some());
1727 assert!(dashboard.get("allocation_details").is_some());
1728 assert!(dashboard.get("type_usage").is_some());
1729 assert!(dashboard.get("unsafe_operations").is_some());
1730 assert!(dashboard.get("analysis_summary").is_some());
1731
1732 let metadata = dashboard.get("metadata").unwrap();
1734 assert_eq!(
1735 metadata.get("total_allocations").unwrap().as_u64().unwrap(),
1736 100
1737 );
1738 assert_eq!(
1739 metadata
1740 .get("active_allocations")
1741 .unwrap()
1742 .as_u64()
1743 .unwrap(),
1744 50
1745 );
1746
1747 let allocation_details = dashboard
1749 .get("allocation_details")
1750 .unwrap()
1751 .as_array()
1752 .unwrap();
1753 assert_eq!(allocation_details.len(), 2);
1754
1755 let first_alloc = &allocation_details[0];
1756 assert_eq!(first_alloc.get("ptr").unwrap().as_str().unwrap(), "0x1000");
1757 assert_eq!(first_alloc.get("size").unwrap().as_u64().unwrap(), 64);
1758 assert_eq!(
1759 first_alloc.get("type_name").unwrap().as_str().unwrap(),
1760 "String"
1761 );
1762 assert_eq!(
1763 first_alloc.get("var_name").unwrap().as_str().unwrap(),
1764 "test_var"
1765 );
1766
1767 assert!(first_alloc.get("borrow_info").is_some());
1769 assert!(first_alloc.get("clone_info").is_some());
1770 assert!(first_alloc.get("ownership_history_available").is_some());
1771 assert!(first_alloc.get("ownership_history").is_some());
1772 }
1773
1774 #[test]
1775 fn test_identify_memory_hotspots() {
1776 let memory_by_type = vec![
1777 TypeMemoryUsage {
1778 type_name: "LargeType".to_string(),
1779 total_size: 2 * 1024 * 1024, current_size: 2 * 1024 * 1024,
1781 allocation_count: 10,
1782 average_size: 204800.0,
1783 peak_size: 512 * 1024,
1784 efficiency_score: 0.7,
1785 },
1786 TypeMemoryUsage {
1787 type_name: "MediumType".to_string(),
1788 total_size: 128 * 1024, current_size: 128 * 1024,
1790 allocation_count: 50,
1791 average_size: 2560.0,
1792 peak_size: 4096,
1793 efficiency_score: 0.8,
1794 },
1795 TypeMemoryUsage {
1796 type_name: "SmallType".to_string(),
1797 total_size: 512, current_size: 512,
1799 allocation_count: 100,
1800 average_size: 5.12,
1801 peak_size: 64,
1802 efficiency_score: 0.9,
1803 },
1804 ];
1805
1806 let hotspots = identify_memory_hotspots(&memory_by_type);
1807
1808 assert_eq!(hotspots.len(), 2);
1810
1811 let first_hotspot = &hotspots[0];
1813 assert_eq!(
1814 first_hotspot.get("type").unwrap().as_str().unwrap(),
1815 "LargeType"
1816 );
1817 assert_eq!(
1818 first_hotspot.get("total_size").unwrap().as_u64().unwrap(),
1819 2 * 1024 * 1024
1820 );
1821 assert_eq!(
1822 first_hotspot.get("severity").unwrap().as_str().unwrap(),
1823 "high"
1824 );
1825
1826 let second_hotspot = &hotspots[1];
1827 assert_eq!(
1828 second_hotspot.get("type").unwrap().as_str().unwrap(),
1829 "MediumType"
1830 );
1831 assert_eq!(
1832 second_hotspot.get("total_size").unwrap().as_u64().unwrap(),
1833 128 * 1024
1834 );
1835 assert_eq!(
1836 second_hotspot.get("severity").unwrap().as_str().unwrap(),
1837 "medium"
1838 );
1839 }
1840
1841 #[test]
1842 fn test_generate_optimization_recommendations() {
1843 let high_frag_stats = MemoryStats {
1845 total_allocations: 100,
1846 total_allocated: 10000,
1847 active_allocations: 70,
1848 active_memory: 3000, peak_allocations: 90,
1850 peak_memory: 8000,
1851 total_deallocations: 30,
1852 total_deallocated: 3000,
1853 leaked_allocations: 0,
1854 leaked_memory: 0,
1855 fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
1856 lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
1857 allocations: Vec::new(),
1858 system_library_stats: crate::core::types::SystemLibraryStats::default(),
1859 concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
1860 };
1861
1862 let memory_by_type = vec![TypeMemoryUsage {
1863 type_name: "LargeType".to_string(),
1864 total_size: 2 * 1024 * 1024,
1865 current_size: 2 * 1024 * 1024,
1866 allocation_count: 1,
1867 average_size: 2.0 * 1024.0 * 1024.0, peak_size: 2 * 1024 * 1024,
1869 efficiency_score: 0.5,
1870 }];
1871
1872 let recommendations =
1873 generate_optimization_recommendations(&high_frag_stats, &memory_by_type);
1874
1875 assert!(!recommendations.is_empty());
1876 assert!(recommendations.iter().any(|r| r.contains("fragmentation")));
1877 assert!(recommendations.iter().any(|r| r.contains("efficiency")));
1878 assert!(recommendations
1879 .iter()
1880 .any(|r| r.contains("large average allocations")));
1881 assert!(recommendations
1882 .iter()
1883 .any(|r| r.contains("allocation-to-deallocation ratio")));
1884
1885 let healthy_stats = MemoryStats {
1887 total_allocations: 100,
1888 total_allocated: 1000,
1889 active_allocations: 10,
1890 active_memory: 900, peak_allocations: 20,
1892 peak_memory: 1000,
1893 total_deallocations: 90,
1894 total_deallocated: 800,
1895 leaked_allocations: 0,
1896 leaked_memory: 0,
1897 fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
1898 lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
1899 allocations: Vec::new(),
1900 system_library_stats: crate::core::types::SystemLibraryStats::default(),
1901 concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
1902 };
1903
1904 let small_memory_by_type = vec![TypeMemoryUsage {
1905 type_name: "SmallType".to_string(),
1906 total_size: 1024,
1907 current_size: 1024,
1908 allocation_count: 10,
1909 average_size: 102.4, peak_size: 256,
1911 efficiency_score: 0.9,
1912 }];
1913
1914 let healthy_recommendations =
1915 generate_optimization_recommendations(&healthy_stats, &small_memory_by_type);
1916 assert!(healthy_recommendations
1917 .iter()
1918 .any(|r| r.contains("healthy")));
1919 }
1920
1921 #[test]
1922 fn test_export_json_options_debug_clone() {
1923 let options = ExportJsonOptions::default();
1924
1925 let debug_str = format!("{:?}", options);
1927 assert!(debug_str.contains("ExportJsonOptions"));
1928 assert!(debug_str.contains("parallel_processing"));
1929 assert!(debug_str.contains("buffer_size"));
1930
1931 let cloned_options = options.clone();
1933 assert_eq!(
1934 cloned_options.parallel_processing,
1935 options.parallel_processing
1936 );
1937 assert_eq!(cloned_options.buffer_size, options.buffer_size);
1938 assert_eq!(cloned_options.batch_size, options.batch_size);
1939 assert_eq!(cloned_options.max_cache_size, options.max_cache_size);
1940 }
1941}