memscope_rs/core/tracker/
export_json.rs

1//! Optimized JSON export functionality for memory tracking data.
2//!
3//! This module provides highly optimized JSON export functionality with performance improvements
4//! including parallel processing, streaming writes, and adaptive optimization.
5
6use 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// Optimized export options with intelligent defaults
20#[derive(Debug, Clone)]
21pub struct ExportJsonOptions {
22    /// Use parallel processing for large datasets
23    pub parallel_processing: bool,
24    /// Buffer size for file I/O
25    pub buffer_size: usize,
26    /// Use compact JSON format for large files
27    pub use_compact_format: Option<bool>,
28    /// Enable type inference caching
29    pub enable_type_cache: bool,
30    /// Batch size for processing allocations
31    pub batch_size: usize,
32    /// Enable streaming writer for large exports
33    pub streaming_writer: bool,
34    /// Enable schema validation
35    pub schema_validation: bool,
36    /// Enable adaptive optimization
37    pub adaptive_optimization: bool,
38    /// Maximum cache size for type information
39    pub max_cache_size: usize,
40    /// Enable security violation analysis
41    pub security_analysis: bool,
42    /// Include low severity violations
43    pub include_low_severity: bool,
44    /// Generate integrity hashes
45    pub integrity_hashes: bool,
46    /// Fast export mode (reduces data quality for speed)
47    pub fast_export_mode: bool,
48    /// Auto-enable fast export for large datasets
49    pub auto_fast_export_threshold: Option<usize>,
50    /// Number of threads for parallel processing
51    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,  // 256KB
59            use_compact_format: None, // Auto-detect
60            enable_type_cache: true,
61            batch_size: 1000,
62            streaming_writer: true,
63            schema_validation: false, // Off by default for speed
64            adaptive_optimization: true,
65            max_cache_size: 10_000,
66            security_analysis: false, // Off by default for speed
67            include_low_severity: false,
68            integrity_hashes: false, // Off by default for speed
69            fast_export_mode: false,
70            auto_fast_export_threshold: Some(10_000), // Auto-enable fast mode for >10k allocations
71            thread_count: None,                       // Use default thread count
72        }
73    }
74}
75
76impl ExportJsonOptions {
77    /// Create new options with specified optimization level
78    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, // 1MB buffer for maximum performance
118                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    // Builder pattern methods for options
136    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    /// Enable or disable streaming writer
157    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    /// Set the batch size for processing allocations
173    pub fn batch_size(mut self, size: usize) -> Self {
174        self.batch_size = size;
175        self
176    }
177
178    /// Enable or disable adaptive optimization
179    pub fn adaptive_optimization(mut self, enabled: bool) -> Self {
180        self.adaptive_optimization = enabled;
181        self
182    }
183
184    /// Set maximum cache size
185    pub fn max_cache_size(mut self, size: usize) -> Self {
186        self.max_cache_size = size;
187        self
188    }
189
190    /// Include low severity violations in reports
191    pub fn include_low_severity(mut self, include: bool) -> Self {
192        self.include_low_severity = include;
193        self
194    }
195
196    /// Set thread count for parallel processing (None for auto-detect)
197    pub fn thread_count(mut self, count: Option<usize>) -> Self {
198        self.thread_count = count;
199        self
200    }
201}
202
203// Type inference cache for performance optimization
204static TYPE_CACHE: std::sync::OnceLock<std::sync::Mutex<HashMap<String, String>>> =
205    std::sync::OnceLock::new();
206
207/// Get cached type information or compute and cache it
208fn 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
224/// Compute enhanced type information
225fn compute_enhanced_type_info(type_name: &str, size: usize) -> String {
226    // Fast path for common types
227    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/// Clear the type cache (useful for testing)
238#[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
247/// Process a batch of allocations (legacy function for compatibility)
248fn 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            // improve.md extensions
263            "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
283/// Enhanced batch processing with new data pipeline integration
284fn 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    // Process in parallel if enabled and batch size is large enough
292    let result = if options.parallel_processing && batch_size > options.batch_size {
293        let chunk_size = (batch_size / num_cpus::get()).max(1);
294
295        // Process chunks in parallel and flatten the results
296        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 everything in a single chunk
311        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
325/// Optimized file writing with streaming support and schema validation
326fn 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    // Validate schema if enabled and not in fast export mode
334    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        // Fast mode: skip validation for better performance
349    }
350
351    // Determine format based on data size
352    let estimated_size = estimate_json_size(data);
353    let use_compact = options
354        .use_compact_format
355        .unwrap_or(estimated_size > 1_000_000); // Use compact for files > 1MB
356
357    // Use streaming writer for large files or when explicitly enabled
358    // Streaming writer implementation for large datasets
359    if options.streaming_writer && estimated_size > 500_000 {
360        let _file = File::create(path)?;
361        // let mut streaming_writer = StreamingJsonWriter::new(file);
362        // streaming_writer.write_complete_json(data)?;
363        // streaming_writer.finalize()?;
364    } else {
365        // Use traditional buffered writer for smaller files
366        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
381/// Estimate JSON size for format decision
382fn estimate_json_size(data: &serde_json::Value) -> usize {
383    // Quick estimation based on structure
384    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    /// Export memory tracking data to 4 separate JSON files.
398    ///
399    /// This method exports data to 4 specialized files:
400    /// - {name}_memory_analysis.json: Memory allocation patterns and statistics
401    /// - {name}_lifetime.json: Variable lifetime and scope analysis
402    /// - {name}_unsafe_ffi.json: Unsafe operations and FFI tracking
403    /// - {name}_variable_relationships.json: Variable dependency graph and relationships
404    ///
405    /// # Export Modes
406    ///
407    /// ## Default Mode (Fast - Recommended)
408    /// ```no_run
409    /// # use memscope_rs::core::get_global_tracker;
410    /// # use memscope_rs::core::tracker::export_json::ExportJsonOptions;
411    /// let tracker = get_global_tracker();
412    /// tracker.export_to_json("output").unwrap();
413    /// // OR explicitly
414    /// tracker.export_to_json_with_options("output", ExportJsonOptions::default()).unwrap();
415    /// ```
416    /// - **Performance**: ~2-5 seconds for typical datasets
417    /// - **Data**: Only user-tracked variables get full enrichment
418    /// - **Use case**: Normal development, HTML rendering, production monitoring
419    ///
420    /// ## Complete Mode (Slow - Debug Only)
421    /// ```no_run
422    /// # use memscope_rs::core::get_global_tracker;
423    /// # use memscope_rs::core::tracker::export_json::ExportJsonOptions;
424    /// let tracker = get_global_tracker();
425    /// let mut options = ExportJsonOptions::default();
426    /// options.security_analysis = true;
427    /// tracker.export_to_json_with_options("output", options).unwrap();
428    /// ```
429    /// - **Performance**: ~10-40 seconds (5-10x slower!)
430    /// - **Data**: ALL allocations including system internals get full enrichment
431    /// - **Use case**: Deep debugging, memory leak investigation, system analysis
432    /// - **⚠️ Warning**: Very slow, generates large files, may impact application performance
433    pub fn export_to_json<P: AsRef<std::path::Path>>(&self, path: P) -> TrackingResult<()> {
434        // CRITICAL FIX: Set export mode to prevent recursive tracking during export
435        thread_local! {
436            static EXPORT_MODE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
437        }
438
439        // Check if already in export mode to prevent nested exports
440        let already_exporting = EXPORT_MODE.with(|mode| mode.get());
441        if already_exporting {
442            return Ok(()); // Skip nested export to prevent recursion
443        }
444
445        // Set export mode
446        EXPORT_MODE.with(|mode| mode.set(true));
447
448        // Ensure output goes to MemoryAnalysis directory
449        let output_path = self.ensure_memory_analysis_path(path);
450
451        // Use fast mode by default for optimal performance
452        let options = ExportJsonOptions::default()
453            .fast_export_mode(true)
454            .security_analysis(false) // Disable for speed
455            .schema_validation(false) // Disable for speed
456            .integrity_hashes(false); // Disable for speed
457
458        // Use the standard export function with our optimized options
459        let result = self.export_to_json_with_options(output_path, options);
460
461        // Clear export mode
462        EXPORT_MODE.with(|mode| mode.set(false));
463
464        result
465    }
466
467    /// Export memory tracking data with custom options.
468    ///
469    /// # Examples
470    ///
471    /// ## Fast mode (default - recommended for most users)
472    /// ```no_run
473    /// # use memscope_rs::core::get_global_tracker;
474    /// # use memscope_rs::core::tracker::export_json::ExportJsonOptions;
475    /// let tracker = get_global_tracker();
476    /// tracker.export_to_json_with_options("output", ExportJsonOptions::default()).unwrap();
477    /// ```
478    ///
479    /// ## Complete mode (slow - for debugging)
480    /// ```no_run
481    /// # use memscope_rs::core::get_global_tracker;
482    /// # use memscope_rs::core::tracker::export_json::ExportJsonOptions;
483    /// let tracker = get_global_tracker();
484    /// let mut options = ExportJsonOptions::default();
485    /// options.security_analysis = true;
486    /// options.schema_validation = true;
487    /// tracker.export_to_json_with_options("debug_output", options).unwrap();
488    /// ```
489    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        // Process allocations based on options
499        let processed = if options.fast_export_mode {
500            process_allocation_batch_enhanced(&allocations, &options)?
501        } else {
502            // Process with full details if not in fast mode
503            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                    // improve.md extensions
511                    "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        // Prepare output data
531        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        // CRITICAL FIX: Ensure parent directory exists before writing
546        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        // Write main memory analysis file
557        let memory_analysis_path = output_path.join("memory_analysis.json");
558        write_json_optimized(memory_analysis_path, &output_data, &options)?;
559
560        // Get memory by type for type analysis
561        let memory_by_type = self.get_memory_by_type()?;
562
563        // Generate additional files as specified in improve.md
564        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    /// Get memory usage by type for export
573    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        // Aggregate memory usage by type
578        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        // Calculate average sizes
602        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    /// Generate lifetime.json with ownership history as specified in improve.md
614    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                        // Generate Allocated event
629                        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                        // Generate Clone events if clone_info is present
641                        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                        // Generate Borrow events if borrow_info is present
661                        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                        // Generate Dropped event if deallocated
698                        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    /// Generate unsafe_ffi.json with FFI safety analysis
734    fn generate_unsafe_ffi_json<P: AsRef<Path>>(
735        &self,
736        output_path: P,
737        options: &ExportJsonOptions,
738    ) -> TrackingResult<()> {
739        // Create default unsafe FFI stats since the method doesn't exist yet
740        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    /// Generate variable_relationships.json with dependency analysis
766    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        // Analyze clone relationships
775        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    /// Generate type_analysis.json with type-based memory analysis
816    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
839/// Build unified dashboard JSON structure compatible with all frontend interfaces
840pub 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    // Calculate performance metrics
848    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; // Convert nanoseconds to milliseconds
861
862    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    // Calculate memory efficiency (active memory / peak memory)
875    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    // Calculate fragmentation ratio (simplified)
882    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    // Prepare allocation details for frontend with extended fields from improve.md
889    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            // Add extended fields from improve.md requirements for user variables
904            if let Some(var_name) = &alloc.var_name {
905                // Add borrow_info for lifetime analysis
906                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                // Add clone_info for ownership analysis
914                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                // Set ownership_history_available flag and generate detailed ownership_history
924                allocation_data["ownership_history_available"] = serde_json::Value::Bool(true);
925
926                // Generate detailed ownership_history for lifetime.json
927                let mut ownership_events = Vec::new();
928
929                // Add allocation event
930                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                // Add clone event if this is a cloned object
938                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                // Add borrow events based on borrow_count
951                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                // Add ownership transfer for smart pointers
963                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                // Add drop event if deallocated
975                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                // Add memory_passport for FFI boundary tracking
987                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    // Prepare unsafe operations for frontend
1028    let unsafe_operations: Vec<_> = unsafe_stats
1029        .operations
1030        .iter()
1031        .take(50) // Limit to avoid huge JSON files
1032        .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    // Prepare type usage data
1043    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    // Build the unified dashboard structure
1057    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
1094/// Identify memory hotspots from type usage data
1095fn 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) // Only consider types using > 1KB
1099        .collect();
1100
1101    // Sort by total size descending
1102    hotspots.sort_by(|a, b| b.total_size.cmp(&a.total_size));
1103
1104    // Take top 10 hotspots
1105    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
1121/// Generate optimization recommendations based on memory statistics
1122pub fn generate_optimization_recommendations(
1123    stats: &MemoryStats,
1124    memory_by_type: &[TypeMemoryUsage],
1125) -> Vec<String> {
1126    let mut recommendations = Vec::new();
1127
1128    // Check for memory fragmentation
1129    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    // Check for memory efficiency
1140    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    // Check for large allocations
1151    let large_allocations = memory_by_type
1152        .iter()
1153        .filter(|usage| usage.average_size > 1024.0 * 1024.0) // > 1MB average
1154        .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    // Check for allocation patterns
1163    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        // Test Low optimization level
1299        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        // Test Medium optimization level (should be same as default)
1311        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        // Test High optimization level
1321        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        // Test Maximum optimization level
1336        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 cache first
1384        clear_type_cache();
1385
1386        // Test cache miss and population
1387        let type_info1 = get_or_compute_type_info("String", 64);
1388        assert_eq!(type_info1, "string");
1389
1390        // Test cache hit (should return same result)
1391        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        // Test different types
1396        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        // Test large allocation
1406        let large_info = get_or_compute_type_info("LargeStruct", 2048);
1407        assert_eq!(large_info, "large");
1408
1409        // Test custom type
1410        let custom_info = get_or_compute_type_info("MyStruct", 64);
1411        assert_eq!(custom_info, "custom");
1412
1413        // Clear cache and verify
1414        clear_type_cache();
1415        let type_info3 = get_or_compute_type_info("String", 64);
1416        assert_eq!(type_info3, "string"); // Should still work after cache clear
1417    }
1418
1419    #[test]
1420    fn test_compute_enhanced_type_info() {
1421        // Test string types
1422        assert_eq!(compute_enhanced_type_info("String", 100), "string");
1423        assert_eq!(compute_enhanced_type_info("&str", 50), "string");
1424
1425        // Test collection types
1426        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        // Test map types
1431        assert_eq!(compute_enhanced_type_info("HashMap", 400), "map");
1432        assert_eq!(compute_enhanced_type_info("BTreeMap", 350), "map");
1433
1434        // Test set types
1435        assert_eq!(compute_enhanced_type_info("HashSet", 250), "set");
1436        assert_eq!(compute_enhanced_type_info("BTreeSet", 200), "set");
1437
1438        // Test large allocations
1439        assert_eq!(compute_enhanced_type_info("LargeBuffer", 2048), "large");
1440        assert_eq!(compute_enhanced_type_info("Unknown", 1500), "large");
1441
1442        // Test custom types
1443        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, // Test unknown type
1464                None, // Test unknown var
1465            ),
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        // Check first allocation
1475        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        // Check second allocation
1484        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        // Check third allocation (unknown type/var)
1492        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        // Verify the processed data structure
1525        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); // Force sequential processing
1541
1542        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        // Test simple object
1552        let simple_obj = serde_json::json!({
1553            "key": "value"
1554        });
1555        let size1 = estimate_json_size(&simple_obj);
1556        assert!(size1 > 0);
1557
1558        // Test array
1559        let array = serde_json::json!([1, 2, 3, 4, 5]);
1560        let size2 = estimate_json_size(&array);
1561        assert!(size2 > 0);
1562
1563        // Test complex nested structure
1564        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        // Test string
1581        let string_val = serde_json::json!("This is a test string");
1582        let size4 = estimate_json_size(&string_val);
1583        assert!(size4 > 20); // String length + overhead
1584
1585        // Test primitive
1586        let number = serde_json::json!(42);
1587        let size5 = estimate_json_size(&number);
1588        assert_eq!(size5, 20); // Default primitive size
1589    }
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); // Use traditional writer for small files
1605
1606        let result = write_json_optimized(&file_path, &test_data, &options);
1607        assert!(result.is_ok());
1608
1609        // Verify file was created and contains valid JSON
1610        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        // Verify file was created
1638        assert!(file_path.exists());
1639        let content = std::fs::read_to_string(&file_path).unwrap();
1640
1641        // Compact format should not have extra whitespace
1642        assert!(!content.contains("  ")); // No indentation
1643        assert!(!content.contains('\n')); // No newlines
1644    }
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        // Verify file was created
1667        assert!(file_path.exists());
1668        let content = std::fs::read_to_string(&file_path).unwrap();
1669
1670        // Pretty format should have whitespace
1671        assert!(content.contains("  ") || content.contains('\n')); // Has formatting
1672    }
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, // Use same for history
1718            &memory_by_type,
1719            &stats,
1720            &unsafe_stats,
1721        );
1722
1723        // Verify structure
1724        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        // Verify metadata
1733        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        // Verify allocation details
1748        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        // Verify extended fields are present
1768        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, // 2MB
1780                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, // 128KB
1789                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, // 512B - should be filtered out
1798                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        // Should only include types > 1KB
1809        assert_eq!(hotspots.len(), 2);
1810
1811        // Should be sorted by total size descending
1812        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        // Test high fragmentation
1844        let high_frag_stats = MemoryStats {
1845            total_allocations: 100,
1846            total_allocated: 10000,
1847            active_allocations: 70,
1848            active_memory: 3000, // Low active vs total allocated
1849            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, // > 1MB average
1868            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        // Test healthy memory usage
1886        let healthy_stats = MemoryStats {
1887            total_allocations: 100,
1888            total_allocated: 1000,
1889            active_allocations: 10,
1890            active_memory: 900, // High efficiency
1891            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, // Small average
1910            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        // Test Debug implementation
1926        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        // Test Clone implementation
1932        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}