memscope_rs/export/binary/
config.rs

1//! Binary export configuration for advanced memory analysis metrics
2
3use serde::{Deserialize, Serialize};
4
5/// Advanced metrics level configuration
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
7pub enum AdvancedMetricsLevel {
8    /// Only basic data (existing behavior)
9    None,
10    /// Core advanced metrics with minimal performance impact
11    #[default]
12    Essential,
13    /// All advanced metrics, may impact performance
14    Comprehensive,
15}
16
17/// Binary export configuration with advanced metrics support
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct BinaryExportConfig {
20    // Existing fields (maintain compatibility)
21    /// Buffer size for I/O operations
22    pub buffer_size: usize,
23    /// Include detailed information
24    pub include_details: bool,
25    /// Compression level (0-9, 0 = no compression)
26    pub compression_level: u8,
27
28    // New advanced metrics fields
29    /// Advanced metrics collection level
30    pub advanced_metrics_level: AdvancedMetricsLevel,
31    /// Enable source code analysis (stack traces with file/line info)
32    pub source_analysis: bool,
33    /// Enable lifecycle timeline analysis
34    pub lifecycle_timeline: bool,
35    /// Enable container structure analysis (Vec, HashMap, etc.)
36    pub container_analysis: bool,
37    /// Enable memory fragmentation analysis
38    pub fragmentation_analysis: bool,
39    /// Enable thread context tracking
40    pub thread_context_tracking: bool,
41    /// Enable Drop chain analysis
42    pub drop_chain_analysis: bool,
43    /// Enable ZST (Zero-Sized Type) analysis
44    pub zst_analysis: bool,
45    /// Enable memory health scoring
46    pub health_scoring: bool,
47    /// Enable performance benchmarking
48    pub performance_benchmarking: bool,
49    /// Enable string table optimization for repeated strings
50    pub string_table_optimization: bool,
51}
52
53impl Default for BinaryExportConfig {
54    fn default() -> Self {
55        Self::performance_first()
56    }
57}
58
59impl BinaryExportConfig {
60    /// Create new configuration with default settings
61    pub fn new() -> Self {
62        Self::default()
63    }
64
65    /// Performance-first configuration (minimal overhead)
66    pub fn performance_first() -> Self {
67        Self {
68            // Basic settings
69            buffer_size: 64 * 1024, // 64KB
70            include_details: true,
71            compression_level: 0, // No compression for speed
72
73            // Advanced metrics - conservative settings
74            advanced_metrics_level: AdvancedMetricsLevel::Essential,
75            source_analysis: false,          // Stack tracing has overhead
76            lifecycle_timeline: true,        // Based on existing data, low overhead
77            container_analysis: true,        // High value container analysis
78            fragmentation_analysis: false,   // Computation overhead
79            thread_context_tracking: true,   // Low overhead, high value
80            drop_chain_analysis: false,      // Can be expensive
81            zst_analysis: false,             // Specialized use case
82            health_scoring: false,           // Additional computation
83            performance_benchmarking: false, // Only for debugging
84            string_table_optimization: true, // Good compression with minimal overhead
85        }
86    }
87
88    /// Debug/development configuration (comprehensive analysis)
89    pub fn debug_comprehensive() -> Self {
90        Self {
91            // Basic settings
92            buffer_size: 128 * 1024, // 128KB for better I/O
93            include_details: true,
94            compression_level: 1, // Light compression
95
96            // Advanced metrics - all enabled
97            advanced_metrics_level: AdvancedMetricsLevel::Comprehensive,
98            source_analysis: true,
99            lifecycle_timeline: true,
100            container_analysis: true,
101            fragmentation_analysis: true,
102            thread_context_tracking: true,
103            drop_chain_analysis: true,
104            zst_analysis: true,
105            health_scoring: true,
106            performance_benchmarking: true,
107            string_table_optimization: true,
108        }
109    }
110
111    /// Minimal configuration (fastest export, basic data only)
112    pub fn minimal() -> Self {
113        Self {
114            // Basic settings
115            buffer_size: 32 * 1024, // 32KB
116            include_details: false,
117            compression_level: 0,
118
119            // Advanced metrics - all disabled
120            advanced_metrics_level: AdvancedMetricsLevel::None,
121            source_analysis: false,
122            lifecycle_timeline: false,
123            container_analysis: false,
124            fragmentation_analysis: false,
125            thread_context_tracking: false,
126            drop_chain_analysis: false,
127            zst_analysis: false,
128            health_scoring: false,
129            performance_benchmarking: false,
130            string_table_optimization: false, // Minimal config disables optimizations
131        }
132    }
133
134    /// Validate configuration and apply safe defaults
135    pub fn validate_and_fix(&mut self) -> Vec<String> {
136        let mut warnings = Vec::new();
137
138        // Validate buffer size
139        if self.buffer_size < 1024 {
140            warnings.push("Buffer size too small, setting to 1KB minimum".to_string());
141            self.buffer_size = 1024;
142        } else if self.buffer_size > 1024 * 1024 {
143            warnings.push("Buffer size too large, setting to 1MB maximum".to_string());
144            self.buffer_size = 1024 * 1024;
145        }
146
147        // Validate compression level
148        if self.compression_level > 9 {
149            warnings.push("Compression level too high, setting to maximum 9".to_string());
150            self.compression_level = 9;
151        }
152
153        // Check for conflicting settings
154        match self.advanced_metrics_level {
155            AdvancedMetricsLevel::None => {
156                // If metrics level is None, disable all advanced features
157                if self.source_analysis
158                    || self.lifecycle_timeline
159                    || self.container_analysis
160                    || self.fragmentation_analysis
161                    || self.thread_context_tracking
162                    || self.drop_chain_analysis
163                    || self.zst_analysis
164                    || self.health_scoring
165                {
166                    warnings.push("Advanced metrics level is None but some advanced features are enabled. Disabling advanced features.".to_string());
167                    self.disable_all_advanced_features();
168                }
169            }
170            AdvancedMetricsLevel::Essential => {
171                // For Essential level, disable expensive features
172                if self.source_analysis {
173                    warnings.push("Source analysis is expensive for Essential level. Consider using Comprehensive level or disabling source analysis.".to_string());
174                }
175                if self.fragmentation_analysis {
176                    warnings.push("Fragmentation analysis is expensive for Essential level. Consider using Comprehensive level or disabling fragmentation analysis.".to_string());
177                }
178            }
179            AdvancedMetricsLevel::Comprehensive => {
180                // All features are allowed at Comprehensive level
181            }
182        }
183
184        // Performance vs features conflict detection
185        if self.compression_level > 0
186            && self.advanced_metrics_level == AdvancedMetricsLevel::Comprehensive
187        {
188            warnings.push(
189                "High compression with comprehensive metrics may significantly impact performance"
190                    .to_string(),
191            );
192        }
193
194        warnings
195    }
196
197    /// Disable all advanced features (used internally)
198    fn disable_all_advanced_features(&mut self) {
199        self.source_analysis = false;
200        self.lifecycle_timeline = false;
201        self.container_analysis = false;
202        self.fragmentation_analysis = false;
203        self.thread_context_tracking = false;
204        self.drop_chain_analysis = false;
205        self.zst_analysis = false;
206        self.health_scoring = false;
207        self.performance_benchmarking = false;
208        self.string_table_optimization = false;
209    }
210
211    /// Check if any advanced metrics are enabled
212    pub fn has_advanced_metrics(&self) -> bool {
213        self.advanced_metrics_level != AdvancedMetricsLevel::None
214            || self.source_analysis
215            || self.lifecycle_timeline
216            || self.container_analysis
217            || self.fragmentation_analysis
218            || self.thread_context_tracking
219            || self.drop_chain_analysis
220            || self.zst_analysis
221            || self.health_scoring
222            || self.performance_benchmarking
223            || self.string_table_optimization
224    }
225
226    /// Get estimated performance impact (0.0 = no impact, 1.0 = significant impact)
227    pub fn estimated_performance_impact(&self) -> f64 {
228        let mut impact = 0.0;
229
230        // Base impact from metrics level
231        impact += match self.advanced_metrics_level {
232            AdvancedMetricsLevel::None => 0.0,
233            AdvancedMetricsLevel::Essential => 0.1,
234            AdvancedMetricsLevel::Comprehensive => 0.3,
235        };
236
237        // Individual feature impacts
238        if self.source_analysis {
239            impact += 0.2;
240        }
241        if self.fragmentation_analysis {
242            impact += 0.3;
243        }
244        if self.drop_chain_analysis {
245            impact += 0.15;
246        }
247        if self.zst_analysis {
248            impact += 0.1;
249        }
250        if self.health_scoring {
251            impact += 0.1;
252        }
253        if self.performance_benchmarking {
254            impact += 0.05;
255        }
256
257        // Compression impact
258        impact += self.compression_level as f64 * 0.02;
259
260        impact.min(1.0) // Cap at 1.0
261    }
262}
263
264/// Builder pattern for BinaryExportConfig
265pub struct BinaryExportConfigBuilder {
266    config: BinaryExportConfig,
267}
268
269impl BinaryExportConfigBuilder {
270    /// Create new config builder with performance-first defaults
271    pub fn new() -> Self {
272        Self {
273            config: BinaryExportConfig::performance_first(),
274        }
275    }
276
277    /// Create builder from existing config
278    pub fn from_config(config: BinaryExportConfig) -> Self {
279        Self { config }
280    }
281
282    /// Set buffer size
283    pub fn buffer_size(mut self, size: usize) -> Self {
284        self.config.buffer_size = size;
285        self
286    }
287
288    /// Set compression level
289    pub fn compression_level(mut self, level: u8) -> Self {
290        self.config.compression_level = level;
291        self
292    }
293
294    /// Set advanced metrics level
295    pub fn advanced_metrics_level(mut self, level: AdvancedMetricsLevel) -> Self {
296        self.config.advanced_metrics_level = level;
297        self
298    }
299
300    /// Enable/disable source analysis
301    pub fn source_analysis(mut self, enable: bool) -> Self {
302        self.config.source_analysis = enable;
303        self
304    }
305
306    /// Enable/disable lifecycle timeline
307    pub fn lifecycle_timeline(mut self, enable: bool) -> Self {
308        self.config.lifecycle_timeline = enable;
309        self
310    }
311
312    /// Enable/disable container analysis
313    pub fn container_analysis(mut self, enable: bool) -> Self {
314        self.config.container_analysis = enable;
315        self
316    }
317
318    /// Enable/disable fragmentation analysis
319    pub fn fragmentation_analysis(mut self, enable: bool) -> Self {
320        self.config.fragmentation_analysis = enable;
321        self
322    }
323
324    /// Enable/disable thread context tracking
325    pub fn thread_context_tracking(mut self, enable: bool) -> Self {
326        self.config.thread_context_tracking = enable;
327        self
328    }
329
330    /// Enable/disable Drop chain analysis
331    pub fn drop_chain_analysis(mut self, enable: bool) -> Self {
332        self.config.drop_chain_analysis = enable;
333        self
334    }
335
336    /// Enable/disable ZST analysis
337    pub fn zst_analysis(mut self, enable: bool) -> Self {
338        self.config.zst_analysis = enable;
339        self
340    }
341
342    /// Enable/disable health scoring
343    pub fn health_scoring(mut self, enable: bool) -> Self {
344        self.config.health_scoring = enable;
345        self
346    }
347
348    /// Enable/disable performance benchmarking
349    pub fn performance_benchmarking(mut self, enable: bool) -> Self {
350        self.config.performance_benchmarking = enable;
351        self
352    }
353
354    /// Enable/disable string table optimization
355    pub fn string_table_optimization(mut self, enable: bool) -> Self {
356        self.config.string_table_optimization = enable;
357        self
358    }
359
360    /// Build the configuration
361    pub fn build(mut self) -> BinaryExportConfig {
362        let warnings = self.config.validate_and_fix();
363        if !warnings.is_empty() {
364            tracing::warn!("Configuration warnings: {:?}", warnings);
365        }
366        self.config
367    }
368}
369
370impl Default for BinaryExportConfigBuilder {
371    fn default() -> Self {
372        Self::new()
373    }
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379
380    #[test]
381    fn test_default_config() {
382        let config = BinaryExportConfig::default();
383        assert_eq!(
384            config.advanced_metrics_level,
385            AdvancedMetricsLevel::Essential
386        );
387        assert!(!config.source_analysis); // Should be false for performance
388        assert!(config.lifecycle_timeline); // Should be true for value
389        assert!(config.container_analysis); // Should be true for value
390    }
391
392    #[test]
393    fn test_performance_first_config() {
394        let config = BinaryExportConfig::performance_first();
395        assert_eq!(config.compression_level, 0);
396        assert!(!config.source_analysis);
397        assert!(!config.fragmentation_analysis);
398        assert!(config.container_analysis); // High value, low cost
399    }
400
401    #[test]
402    fn test_debug_comprehensive_config() {
403        let config = BinaryExportConfig::debug_comprehensive();
404        assert_eq!(
405            config.advanced_metrics_level,
406            AdvancedMetricsLevel::Comprehensive
407        );
408        assert!(config.source_analysis);
409        assert!(config.fragmentation_analysis);
410        assert!(config.zst_analysis);
411    }
412
413    #[test]
414    fn test_minimal_config() {
415        let config = BinaryExportConfig::minimal();
416        assert_eq!(config.advanced_metrics_level, AdvancedMetricsLevel::None);
417        assert!(!config.source_analysis);
418        assert!(!config.lifecycle_timeline);
419        assert!(!config.container_analysis);
420    }
421
422    #[test]
423    fn test_config_validation() {
424        let mut config = BinaryExportConfig {
425            buffer_size: 100,
426            compression_level: 15,
427            ..Default::default()
428        };
429
430        let warnings = config.validate_and_fix();
431        assert!(!warnings.is_empty());
432        assert_eq!(config.buffer_size, 1024);
433        assert_eq!(config.compression_level, 9);
434    }
435
436    #[test]
437    fn test_config_builder() {
438        let config = BinaryExportConfigBuilder::new()
439            .advanced_metrics_level(AdvancedMetricsLevel::Comprehensive)
440            .source_analysis(true)
441            .fragmentation_analysis(true)
442            .compression_level(3)
443            .build();
444
445        assert_eq!(
446            config.advanced_metrics_level,
447            AdvancedMetricsLevel::Comprehensive
448        );
449        assert!(config.source_analysis);
450        assert!(config.fragmentation_analysis);
451        assert_eq!(config.compression_level, 3);
452    }
453
454    #[test]
455    fn test_performance_impact_estimation() {
456        let minimal = BinaryExportConfig::minimal();
457        let comprehensive = BinaryExportConfig::debug_comprehensive();
458
459        assert!(minimal.estimated_performance_impact() < 0.1);
460        assert!(comprehensive.estimated_performance_impact() > 0.5);
461    }
462
463    #[test]
464    fn test_has_advanced_metrics() {
465        let minimal = BinaryExportConfig::minimal();
466        let performance = BinaryExportConfig::performance_first();
467        let comprehensive = BinaryExportConfig::debug_comprehensive();
468
469        assert!(!minimal.has_advanced_metrics());
470        assert!(performance.has_advanced_metrics());
471        assert!(comprehensive.has_advanced_metrics());
472    }
473}
474
475/// Dashboard export format options
476#[derive(Debug, Clone, PartialEq, Default)]
477pub enum DashboardFormat {
478    /// Current embedded format - all data in HTML (backward compatible)
479    Embedded,
480    /// Lightweight format - HTML + separate JSON files
481    #[default]
482    Lightweight,
483    /// Progressive format - HTML + lazy-loaded JSON files
484    Progressive,
485}
486
487/// Data scope for export
488#[derive(Debug, Clone, PartialEq, Default)]
489pub enum DataScope {
490    /// Only user allocations (with var_name)
491    UserOnly,
492    /// Only system allocations (without var_name)
493    SystemOnly,
494    /// Both user and system allocations
495    #[default]
496    Both,
497}
498
499/// Performance mode for analysis
500#[derive(Debug, Clone, PartialEq, Default)]
501pub enum PerformanceMode {
502    /// Fast mode - basic analysis only
503    Fast,
504    /// Complete mode - all analysis types
505    #[default]
506    Complete,
507    /// Custom mode - specify which analysis to include
508    Custom(Vec<AnalysisType>),
509}
510
511/// Analysis types that can be included/excluded
512#[derive(Debug, Clone, PartialEq)]
513pub enum AnalysisType {
514    MemoryAnalysis,
515    LifecycleTracking,
516    VariableRelationships,
517    ComplexTypes,
518    UnsafeFFI,
519    PerformanceMetrics,
520}
521
522/// Unified dashboard export options
523#[derive(Debug, Clone, Default)]
524pub struct DashboardOptions {
525    /// Export format (embedded, lightweight, progressive)
526    pub format: DashboardFormat,
527    /// Data scope (user, system, both)
528    pub scope: DataScope,
529    /// Performance mode (fast, complete, custom)
530    pub performance: PerformanceMode,
531    /// Custom output directory (None for auto-generated)
532    pub output_dir: Option<std::path::PathBuf>,
533    /// Underlying binary export config
534    pub binary_config: BinaryExportConfig,
535}
536
537impl DashboardOptions {
538    /// Create new dashboard options with default settings
539    pub fn new() -> Self {
540        Self::default()
541    }
542
543    /// Set the export format
544    pub fn format(mut self, format: DashboardFormat) -> Self {
545        self.format = format;
546        self
547    }
548
549    /// Set the data scope
550    pub fn scope(mut self, scope: DataScope) -> Self {
551        self.scope = scope;
552        self
553    }
554
555    /// Set the performance mode
556    pub fn performance(mut self, performance: PerformanceMode) -> Self {
557        self.performance = performance;
558        self
559    }
560
561    /// Set custom output directory
562    pub fn output_dir<P: Into<std::path::PathBuf>>(mut self, dir: P) -> Self {
563        self.output_dir = Some(dir.into());
564        self
565    }
566
567    /// Set underlying binary export config
568    pub fn binary_config(mut self, config: BinaryExportConfig) -> Self {
569        self.binary_config = config;
570        self
571    }
572
573    /// Enable parallel processing
574    pub fn parallel_processing(self, _enabled: bool) -> Self {
575        // Parallel processing configuration for BinaryExportConfig
576        self
577    }
578
579    /// Set batch size
580    pub fn batch_size(self, _size: usize) -> Self {
581        // Batch size configuration for optimal processing performance
582        self
583    }
584
585    /// Set buffer size
586    pub fn buffer_size(mut self, size: usize) -> Self {
587        self.binary_config.buffer_size = size;
588        self
589    }
590
591    /// Quick preset for fast export (minimal analysis)
592    pub fn fast_preset() -> Self {
593        Self {
594            format: DashboardFormat::Lightweight,
595            scope: DataScope::UserOnly,
596            performance: PerformanceMode::Fast,
597            output_dir: None,
598            binary_config: BinaryExportConfig::performance_first(),
599        }
600    }
601
602    /// Quick preset for complete analysis
603    pub fn complete_preset() -> Self {
604        Self {
605            format: DashboardFormat::Progressive,
606            scope: DataScope::Both,
607            performance: PerformanceMode::Complete,
608            output_dir: None,
609            binary_config: BinaryExportConfig::debug_comprehensive(),
610        }
611    }
612
613    /// Quick preset for backward compatibility (embedded format)
614    pub fn embedded_preset() -> Self {
615        Self {
616            format: DashboardFormat::Embedded,
617            scope: DataScope::Both,
618            performance: PerformanceMode::Complete,
619            output_dir: None,
620            binary_config: BinaryExportConfig::default(),
621        }
622    }
623}
624
625/// Dashboard export statistics
626#[derive(Debug, Clone)]
627pub struct DashboardExportStats {
628    /// Total number of files generated
629    pub total_files_generated: usize,
630    /// Size of main HTML file
631    pub html_size: usize,
632    /// Total size of JSON files (if separated)
633    pub total_json_size: usize,
634    /// Total processing time in milliseconds
635    pub processing_time_ms: u64,
636    /// Number of allocations processed
637    pub allocations_processed: usize,
638    /// Export format used
639    pub format_used: DashboardFormat,
640    /// Data scope used
641    pub scope_used: DataScope,
642}