memscope_rs/export/
fixed_hybrid_template.rs

1//! Clean Template Renderer - Only data injection, no embedded HTML
2//! All HTML and JS code moved to external templates
3
4use super::fixed_html::{get_hybrid_dashboard_template, ENHANCED_DIAGNOSTICS, JS};
5use crate::async_memory::visualization::VisualizationConfig;
6use crate::lockfree::LockfreeAnalysis;
7use std::collections::HashMap;
8
9/// Combined analysis data from multiple sources
10#[derive(Debug)]
11pub struct HybridAnalysisData {
12    pub lockfree_analysis: Option<LockfreeAnalysis>,
13    pub visualization_config: VisualizationConfig,
14    pub thread_task_mapping: HashMap<usize, Vec<usize>>,
15    pub variable_registry: HashMap<String, VariableDetail>,
16    pub performance_metrics: PerformanceTimeSeries,
17}
18
19/// Real-time performance metrics collection
20#[derive(Debug)]
21pub struct PerformanceTimeSeries {
22    pub cpu_usage: Vec<f64>,
23    pub memory_usage: Vec<u64>,
24    pub io_operations: Vec<u64>,
25    pub network_bytes: Vec<u64>,
26    pub timestamps: Vec<u64>,
27    pub thread_cpu_breakdown: HashMap<usize, Vec<f64>>,
28    pub thread_memory_breakdown: HashMap<usize, Vec<u64>>,
29}
30
31/// Detailed variable information for template rendering
32#[derive(Debug, Clone)]
33pub struct VariableDetail {
34    pub name: String,
35    pub type_info: String,
36    pub thread_id: usize,
37    pub task_id: Option<usize>,
38    pub allocation_count: u64,
39    pub memory_usage: u64,
40    pub lifecycle_stage: LifecycleStage,
41}
42
43/// Variable lifecycle tracking stages
44#[derive(Debug, Clone)]
45pub enum LifecycleStage {
46    Allocated,
47    Active,
48    Shared,
49    Deallocated,
50}
51
52/// Render mode for different visualization types
53#[derive(Debug, Clone)]
54pub enum RenderMode {
55    Comprehensive,
56    ThreadFocused,
57    VariableDetailed,
58}
59
60pub struct FixedHybridTemplate {
61    pub output_path: String,
62    pub thread_count: usize,
63    pub task_count: usize,
64    pub render_mode: RenderMode,
65}
66
67impl FixedHybridTemplate {
68    pub fn new(thread_count: usize, task_count: usize) -> Self {
69        Self {
70            output_path: "simple_hybrid_dashboard_variable_detailed.html".to_string(),
71            thread_count,
72            task_count,
73            render_mode: RenderMode::VariableDetailed,
74        }
75    }
76
77    pub fn with_render_mode(mut self, mode: RenderMode) -> Self {
78        self.render_mode = mode;
79        self
80    }
81
82    pub fn with_variable_details(self, _enable: bool) -> Self {
83        // Variable details are always enabled in this simplified version
84        self
85    }
86
87    pub fn with_enhanced_insights(self, _enable: bool) -> Self {
88        // Enhanced insights will be processed in render_with_template
89        self
90    }
91
92    /// Generate hybrid dashboard HTML using external template
93    pub fn generate_hybrid_dashboard(
94        &self,
95        data: &HybridAnalysisData,
96    ) -> Result<String, Box<dyn std::error::Error>> {
97        // Load external template
98        // Use embedded template to avoid external file dependency
99        let template_content = get_hybrid_dashboard_template()
100            .replace("{JS}", JS)
101            .replace("{ENHANCED_DIAGNOSTICS}", ENHANCED_DIAGNOSTICS);
102
103        Ok(self.render_with_template(&template_content, data))
104    }
105
106    /// Render using external template
107    fn render_with_template(&self, template: &str, data: &HybridAnalysisData) -> String {
108        let mut html = template.to_string();
109
110        // Replace placeholders with data
111        html = html.replace("{{TITLE}}", "🔬 Memory Analysis Dashboard");
112        html = html.replace(
113            "{{TOTAL_MEMORY}}",
114            &format!("{:.1}MB", self.calculate_total_memory(data)),
115        );
116        html = html.replace(
117            "{{TOTAL_VARIABLES}}",
118            &data.variable_registry.len().to_string(),
119        );
120        html = html.replace(
121            "{{THREAD_COUNT}}",
122            &data
123                .lockfree_analysis
124                .as_ref()
125                .map_or(0, |a| a.thread_stats.len())
126                .to_string(),
127        );
128        html = html.replace("{{EFFICIENCY}}", "85.2");
129
130        // Generate content
131        let variables: Vec<VariableDetail> = data.variable_registry.values().cloned().collect();
132        html = html.replace(
133            "{{VARIABLES_HTML}}",
134            &self.generate_variables_html(&variables),
135        );
136        html = html.replace("{{MEMORY_MAP_HTML}}", &self.generate_memory_map_html(data));
137
138        // Generate JSON data for JavaScript
139        html = html.replace(
140            "{{VARIABLES_DATA}}",
141            &self.serialize_variables_for_js(&variables),
142        );
143        html = html.replace("{{THREADS_DATA}}", &self.serialize_threads_for_js(data));
144        html = html.replace("{{TASKS_DATA}}", &self.serialize_tasks_for_js(data));
145
146        // Add enhanced insights data replacements
147        html = self.replace_insights_placeholders(html, data);
148
149        html
150    }
151
152    /// Calculate total memory usage - use calculation method consistent with comprehensive_export
153    fn calculate_total_memory(&self, data: &HybridAnalysisData) -> f64 {
154        // Use peak_memory data from lockfree_analysis, consistent with comprehensive_export
155        if let Some(analysis) = &data.lockfree_analysis {
156            analysis.summary.peak_memory_usage as f64 / 1024.0 / 1024.0
157        } else {
158            // Fallback to variable_registry calculation
159            data.variable_registry
160                .values()
161                .map(|v| v.memory_usage as f64 / 1024.0 / 1024.0)
162                .sum()
163        }
164    }
165
166    /// Generate variables HTML with performance categories
167    fn generate_variables_html(&self, variables: &[VariableDetail]) -> String {
168        let mut html = String::new();
169
170        for variable in variables.iter() {
171            let status_class = match variable.lifecycle_stage {
172                LifecycleStage::Active => "status-active",
173                LifecycleStage::Allocated => "status-allocated",
174                LifecycleStage::Shared => "status-shared",
175                LifecycleStage::Deallocated => "status-deallocated",
176            };
177
178            let status_icon = match variable.lifecycle_stage {
179                LifecycleStage::Active => "🟢",
180                LifecycleStage::Allocated => "🟡",
181                LifecycleStage::Shared => "🔄 ",
182                LifecycleStage::Deallocated => "⚫",
183            };
184
185            let performance_category = self.classify_variable_performance(variable);
186            let category_class = match performance_category.as_str() {
187                "cpu" => "cpu-intensive",
188                "io" => "io-intensive",
189                "memory" => "memory-intensive",
190                "async" => "async-heavy",
191                _ => "normal",
192            };
193
194            let size_kb = variable.memory_usage / 1024;
195
196            html.push_str(&format!(
197                r#"<div class="variable-card {}" data-category="{}" data-thread="{}" data-memory="{}" data-allocations="{}" onclick="window.drillDown('{}', 'memory')">
198                    <div class="variable-name">{} {}</div>
199                    <div class="variable-info">
200                        <span class="{}">{}KB | {} allocs | {}</span>
201                        <span>Thread {}</span>
202                    </div>
203                    <div class="performance-indicator">
204                        <span class="perf-badge {}">{}</span>
205                    </div>
206                </div>"#,
207                category_class,
208                performance_category,
209                variable.thread_id,
210                size_kb,
211                variable.allocation_count,
212                variable.name,
213                status_icon,
214                variable.name,
215                status_class,
216                size_kb,
217                variable.allocation_count,
218                match variable.lifecycle_stage {
219                    LifecycleStage::Active => "Active",
220                    LifecycleStage::Allocated => "Allocated",
221                    LifecycleStage::Shared => "Shared", 
222                    LifecycleStage::Deallocated => "Deallocated"
223                },
224                variable.thread_id,
225                category_class,
226                self.get_performance_label(&performance_category)
227            ));
228        }
229
230        html
231    }
232
233    /// Classify performance type based on variable characteristics
234    fn classify_variable_performance(&self, variable: &VariableDetail) -> String {
235        let size_kb = variable.memory_usage / 1024;
236        let allocation_rate = variable.allocation_count;
237
238        // Intelligent classification based on variable name and characteristics
239        let var_name = variable.name.to_lowercase();
240
241        if var_name.contains("buffer") || var_name.contains("cache") || size_kb > 500 {
242            "memory".to_string()
243        } else if var_name.contains("cpu") || var_name.contains("compute") || allocation_rate > 100
244        {
245            "cpu".to_string()
246        } else if var_name.contains("io") || var_name.contains("file") || var_name.contains("net") {
247            "io".to_string()
248        } else if var_name.contains("async")
249            || var_name.contains("future")
250            || var_name.contains("task")
251        {
252            "async".to_string()
253        } else {
254            "normal".to_string()
255        }
256    }
257
258    fn get_performance_label(&self, category: &str) -> &str {
259        match category {
260            "cpu" => "CPU",
261            "io" => "I/O",
262            "memory" => "MEM",
263            "async" => "ASYNC",
264            _ => "NORM",
265        }
266    }
267
268    /// Generate memory map HTML
269    fn generate_memory_map_html(&self, data: &HybridAnalysisData) -> String {
270        let mut html = String::new();
271        html.push_str("<div class='memory-map-grid'>");
272
273        // Group by threads and show memory blocks
274        let mut thread_groups = std::collections::HashMap::new();
275        for variable in data.variable_registry.values() {
276            thread_groups
277                .entry(variable.thread_id)
278                .or_insert_with(Vec::new)
279                .push(variable);
280        }
281
282        for (thread_id, thread_vars) in thread_groups.iter().take(8) {
283            let total_memory: u64 = thread_vars.iter().map(|v| v.memory_usage).sum();
284            let total_memory_mb = total_memory as f64 / 1024.0 / 1024.0;
285
286            html.push_str(&format!(
287                r#"<div class="memory-thread-block">
288                    <h4>Thread {} ({:.1}MB)</h4>
289                    <div class="thread-variables">"#,
290                thread_id, total_memory_mb
291            ));
292
293            for variable in thread_vars.iter().take(10) {
294                let size_kb = variable.memory_usage / 1024;
295                html.push_str(&format!(
296                    r#"<div class="memory-var-block" style="width: {}px; height: 20px; background: var(--primary); margin: 2px; display: inline-block; opacity: 0.7;" title="{}: {}KB"></div>"#,
297                    (size_kb / 10).clamp(10, 100),
298                    variable.name,
299                    size_kb
300                ));
301            }
302
303            html.push_str("</div></div>");
304        }
305
306        html.push_str("</div>");
307        html
308    }
309
310    /// Serialize variables for JavaScript
311    fn serialize_variables_for_js(&self, variables: &[VariableDetail]) -> String {
312        let mut json_items = Vec::new();
313
314        for variable in variables.iter() {
315            json_items.push(format!(
316                r#"{{"name":"{}","size":{},"thread":{},"state":"{}","allocs":{}}}"#,
317                variable.name,
318                variable.memory_usage,
319                variable.thread_id,
320                match variable.lifecycle_stage {
321                    LifecycleStage::Active => "Active",
322                    LifecycleStage::Allocated => "Allocated",
323                    LifecycleStage::Shared => "Shared",
324                    LifecycleStage::Deallocated => "Deallocated",
325                },
326                variable.allocation_count
327            ));
328        }
329
330        format!("[{}]", json_items.join(","))
331    }
332
333    /// Serialize threads for JavaScript
334    fn serialize_threads_for_js(&self, data: &HybridAnalysisData) -> String {
335        let mut thread_data = std::collections::HashMap::new();
336
337        for variable in data.variable_registry.values() {
338            let entry = thread_data
339                .entry(variable.thread_id)
340                .or_insert_with(|| (0usize, 0usize));
341            // Convert bytes to KB for reasonable display units
342            entry.0 += (variable.memory_usage as usize + 512) / 1024; // Round to nearest KB
343            entry.1 += 1;
344        }
345
346        let mut json_items = Vec::new();
347        for (thread_id, (memory_kb, count)) in thread_data {
348            json_items.push(format!(
349                r#"{{"id":{},"memory":{},"variables":{}}}"#,
350                thread_id, memory_kb, count
351            ));
352        }
353
354        format!("[{}]", json_items.join(","))
355    }
356
357    /// Serialize tasks for JavaScript
358    fn serialize_tasks_for_js(&self, data: &HybridAnalysisData) -> String {
359        let mut task_data = std::collections::HashMap::new();
360
361        for variable in data.variable_registry.values() {
362            if let Some(task_id) = variable.task_id {
363                let entry = task_data
364                    .entry(task_id)
365                    .or_insert_with(|| (0usize, 0usize, variable.thread_id));
366                // Convert bytes to KB for reasonable display units
367                entry.0 += (variable.memory_usage as usize + 512) / 1024; // Round to nearest KB
368                entry.1 += 1;
369            }
370        }
371
372        let mut json_items = Vec::new();
373        for (task_id, (memory, count, thread_id)) in task_data {
374            json_items.push(format!(
375                r#"{{"id":{},"memory":{},"variables":{},"thread":{}}}"#,
376                task_id, memory, count, thread_id
377            ));
378        }
379
380        format!("[{}]", json_items.join(","))
381    }
382
383    /// Replace all insight placeholders with actual data
384    fn replace_insights_placeholders(&self, mut html: String, data: &HybridAnalysisData) -> String {
385        // Find high usage thread and metrics
386        let (high_usage_thread, max_allocation_size, high_frequency) =
387            self.analyze_high_usage(data);
388        let (small_alloc_count, small_alloc_rate) = self.analyze_small_allocations(data);
389        let (bottleneck_thread, bottleneck_rate, bottleneck_percent) =
390            self.analyze_bottlenecks(data);
391
392        // Replace hotspot variables
393        html = html.replace("{{HIGH_USAGE_THREAD}}", &high_usage_thread.to_string());
394        html = html.replace("{{MAX_ALLOCATION_SIZE}}", &max_allocation_size.to_string());
395        html = html.replace("{{HIGH_FREQUENCY}}", &high_frequency.to_string());
396        html = html.replace("{{SMALL_ALLOC_COUNT}}", &small_alloc_count.to_string());
397        html = html.replace("{{SMALL_ALLOC_RATE}}", &format!("{}/sec", small_alloc_rate));
398
399        // Replace leak detection variables
400        let (leak_status_class, leak_icon, leak_status_title, leak_status_description) =
401            self.analyze_memory_leaks(data);
402        html = html.replace("{{LEAK_STATUS_CLASS}}", &leak_status_class);
403        html = html.replace("{{LEAK_ICON}}", &leak_icon);
404        html = html.replace("{{LEAK_STATUS_TITLE}}", &leak_status_title);
405        html = html.replace("{{LEAK_STATUS_DESCRIPTION}}", &leak_status_description);
406
407        // Replace memory analysis variables
408        let memory_efficiency = self.calculate_memory_efficiency(data);
409        let memory_overhead = self.calculate_memory_overhead(data);
410        let potential_leaks = self.count_potential_leaks(data);
411
412        html = html.replace(
413            "{{MEMORY_EFFICIENCY}}",
414            &format!("{:.1}", memory_efficiency),
415        );
416        html = html.replace("{{MEMORY_OVERHEAD}}", &format!("{:.2}MB", memory_overhead));
417        html = html.replace("{{POTENTIAL_LEAKS}}", &potential_leaks.to_string());
418
419        // Replace performance variables
420        html = html.replace("{{BOTTLENECK_THREAD}}", &bottleneck_thread.to_string());
421        html = html.replace("{{BOTTLENECK_RATE}}", &format!("{:.0}", bottleneck_rate));
422        html = html.replace(
423            "{{BOTTLENECK_PERCENT}}",
424            &format!("{:.0}", bottleneck_percent),
425        );
426        html = html.replace("{{BOTTLENECK_LOCATION}}", "execute_track_var_workload()");
427
428        // Replace suggestion variables
429        let suggested_capacity = self.calculate_suggested_capacity(data);
430        let string_capacity = self.calculate_string_capacity(data);
431        html = html.replace("{{SUGGESTED_CAPACITY}}", &suggested_capacity.to_string());
432        html = html.replace("{{STRING_CAPACITY}}", &string_capacity.to_string());
433
434        // Replace thread rate analysis
435        let (thread_0_9_rate, thread_10_19_rate, thread_20_rate) = self.analyze_thread_rates(data);
436        html = html.replace("{{THREAD_0_9_RATE}}", &format!("{:.0}", thread_0_9_rate));
437        html = html.replace(
438            "{{THREAD_10_19_RATE}}",
439            &format!("{:.0}", thread_10_19_rate),
440        );
441        html = html.replace("{{THREAD_20_RATE}}", &format!("{:.0}", thread_20_rate));
442
443        // Replace scoring variables
444        let (memory_score, allocation_score, thread_score, overall_score) =
445            self.calculate_scores(data);
446        html = html.replace("{{MEMORY_SCORE}}", &memory_score.to_string());
447        html = html.replace("{{ALLOCATION_SCORE}}", &allocation_score.to_string());
448        html = html.replace("{{THREAD_SCORE}}", &thread_score.to_string());
449        html = html.replace("{{OVERALL_SCORE}}", &overall_score.to_string());
450
451        // Replace badge classes
452        let rate_badge_class = if thread_0_9_rate > 1000.0 {
453            "high"
454        } else {
455            "medium"
456        };
457        let overall_rate_status = if thread_0_9_rate > 1000.0 {
458            "High Load"
459        } else {
460            "Normal"
461        };
462        let overall_score_class = if overall_score > 80 {
463            "low"
464        } else if overall_score > 60 {
465            "medium"
466        } else {
467            "high"
468        };
469
470        html = html.replace("{{RATE_BADGE_CLASS}}", rate_badge_class);
471        html = html.replace("{{OVERALL_RATE_STATUS}}", overall_rate_status);
472        html = html.replace("{{OVERALL_SCORE_CLASS}}", overall_score_class);
473
474        // Replace advanced pattern variables
475        html = self.replace_advanced_pattern_variables(html, data);
476
477        // Replace cross-process analysis variables
478        html = self.replace_cross_process_variables(html, data);
479
480        html
481    }
482
483    // Analysis helper methods
484    fn analyze_high_usage(&self, data: &HybridAnalysisData) -> (usize, u64, u64) {
485        let mut max_thread = 0;
486        let mut max_memory = 0u64;
487        let mut max_frequency = 0u64;
488
489        for var in data.variable_registry.values() {
490            if var.memory_usage > max_memory {
491                max_memory = var.memory_usage;
492                max_thread = var.thread_id;
493            }
494            if var.allocation_count > max_frequency {
495                max_frequency = var.allocation_count;
496            }
497        }
498
499        (max_thread, max_memory / 1024, max_frequency) // Convert to KB
500    }
501
502    fn analyze_small_allocations(&self, data: &HybridAnalysisData) -> (u64, u64) {
503        let small_allocations: Vec<_> = data
504            .variable_registry
505            .values()
506            .filter(|v| v.memory_usage < 50 * 1024) // < 50KB
507            .collect();
508
509        let count = small_allocations.len() as u64;
510        let rate = small_allocations
511            .iter()
512            .map(|v| v.allocation_count)
513            .sum::<u64>()
514            / 10; // Simulated rate
515
516        (count, rate)
517    }
518
519    fn analyze_bottlenecks(&self, data: &HybridAnalysisData) -> (usize, f64, f64) {
520        let mut thread_loads = std::collections::HashMap::new();
521
522        for var in data.variable_registry.values() {
523            *thread_loads.entry(var.thread_id).or_insert(0u64) += var.allocation_count;
524        }
525
526        let max_thread = thread_loads
527            .iter()
528            .max_by_key(|(_, &count)| count)
529            .map(|(&thread_id, _)| thread_id)
530            .unwrap_or(0);
531
532        let max_rate = thread_loads.values().max().unwrap_or(&0) * 10; // Simulated rate/sec
533        let avg_rate = if thread_loads.is_empty() {
534            0
535        } else {
536            thread_loads.values().sum::<u64>() / thread_loads.len() as u64 * 10
537        };
538        let percent_above = if avg_rate > 0 {
539            ((max_rate as f64 - avg_rate as f64) / avg_rate as f64) * 100.0
540        } else {
541            0.0
542        };
543
544        (max_thread, max_rate as f64, percent_above)
545    }
546
547    fn analyze_memory_leaks(&self, data: &HybridAnalysisData) -> (String, String, String, String) {
548        let potential_leaks = data
549            .variable_registry
550            .values()
551            .filter(|v| matches!(v.lifecycle_stage, LifecycleStage::Allocated))
552            .count();
553
554        if potential_leaks == 0 {
555            (
556                "clean".to_string(),
557                "✅ ".to_string(),
558                "No Memory Leaks Detected".to_string(),
559                "All tracked variables have proper lifecycle management".to_string(),
560            )
561        } else if potential_leaks < 5 {
562            (
563                "warning".to_string(),
564                "⚠️".to_string(),
565                format!("{} Potential Issues", potential_leaks),
566                "Some variables may not be properly deallocated".to_string(),
567            )
568        } else {
569            (
570                "critical".to_string(),
571                "🚨".to_string(),
572                format!("{} Memory Leaks Found", potential_leaks),
573                "Multiple variables are not being properly deallocated".to_string(),
574            )
575        }
576    }
577
578    fn calculate_memory_efficiency(&self, data: &HybridAnalysisData) -> f64 {
579        let active_vars = data
580            .variable_registry
581            .values()
582            .filter(|v| {
583                matches!(
584                    v.lifecycle_stage,
585                    LifecycleStage::Active | LifecycleStage::Shared
586                )
587            })
588            .count();
589        let total_vars = data.variable_registry.len();
590
591        if total_vars > 0 {
592            (active_vars as f64 / total_vars as f64) * 100.0
593        } else {
594            100.0
595        }
596    }
597
598    fn calculate_memory_overhead(&self, _data: &HybridAnalysisData) -> f64 {
599        0.15 // Simulated overhead in MB
600    }
601
602    fn count_potential_leaks(&self, data: &HybridAnalysisData) -> usize {
603        data.variable_registry
604            .values()
605            .filter(|v| matches!(v.lifecycle_stage, LifecycleStage::Allocated))
606            .count()
607    }
608
609    fn calculate_suggested_capacity(&self, data: &HybridAnalysisData) -> usize {
610        let avg_vec_size = data
611            .variable_registry
612            .values()
613            .filter(|v| v.type_info.contains("Vec"))
614            .map(|v| v.memory_usage / 1024)
615            .collect::<Vec<_>>();
616
617        if !avg_vec_size.is_empty() {
618            (avg_vec_size.iter().sum::<u64>() / avg_vec_size.len() as u64) as usize
619        } else {
620            1024
621        }
622    }
623
624    fn calculate_string_capacity(&self, data: &HybridAnalysisData) -> usize {
625        let avg_string_size = data
626            .variable_registry
627            .values()
628            .filter(|v| v.type_info.contains("String") || v.name.contains("string"))
629            .map(|v| v.memory_usage)
630            .collect::<Vec<_>>();
631
632        if !avg_string_size.is_empty() {
633            (avg_string_size.iter().sum::<u64>() / avg_string_size.len() as u64) as usize
634        } else {
635            256
636        }
637    }
638
639    fn analyze_thread_rates(&self, data: &HybridAnalysisData) -> (f64, f64, f64) {
640        let mut thread_groups = [Vec::new(), Vec::new(), Vec::new()];
641
642        for var in data.variable_registry.values() {
643            let group = if var.thread_id <= 9 {
644                0
645            } else if var.thread_id <= 19 {
646                1
647            } else {
648                2
649            };
650            thread_groups[group].push(var.allocation_count);
651        }
652
653        let rates: Vec<f64> = thread_groups
654            .iter()
655            .map(|group| {
656                if !group.is_empty() {
657                    group.iter().sum::<u64>() as f64 / group.len() as f64 * 50.0
658                // Simulated rate
659                } else {
660                    0.0
661                }
662            })
663            .collect();
664
665        (rates[0], rates[1], rates[2])
666    }
667
668    fn calculate_scores(&self, data: &HybridAnalysisData) -> (u8, u8, u8, u8) {
669        let efficiency = self.calculate_memory_efficiency(data);
670        let memory_score = (efficiency * 0.9) as u8; // Memory efficiency score
671
672        let avg_allocs = data
673            .variable_registry
674            .values()
675            .map(|v| v.allocation_count)
676            .sum::<u64>()
677            / data.variable_registry.len().max(1) as u64;
678        let allocation_score = if avg_allocs < 50 {
679            90
680        } else if avg_allocs < 100 {
681            75
682        } else {
683            60
684        };
685
686        let unique_threads = data
687            .variable_registry
688            .values()
689            .map(|v| v.thread_id)
690            .collect::<std::collections::HashSet<_>>()
691            .len();
692        let thread_score = if unique_threads > 10 { 85 } else { 70 };
693
694        let overall_score = (memory_score
695            .saturating_add(allocation_score)
696            .saturating_add(thread_score))
697            / 3;
698
699        (memory_score, allocation_score, thread_score, overall_score)
700    }
701
702    /// Replace advanced pattern analysis variables
703    fn replace_advanced_pattern_variables(
704        &self,
705        mut html: String,
706        data: &HybridAnalysisData,
707    ) -> String {
708        // Analyze cloning patterns
709        let (clone_var, clone_count, clone_threads, clone_memory_impact, clone_perf_impact) =
710            self.analyze_cloning_patterns(data);
711
712        html = html.replace("{{CLONE_VARIABLE_NAME}}", &clone_var);
713        html = html.replace("{{CLONE_COUNT}}", &clone_count.to_string());
714        html = html.replace("{{CLONE_THREADS}}", &clone_threads.to_string());
715        html = html.replace(
716            "{{CLONE_MEMORY_IMPACT}}",
717            &format!("{:.1}", clone_memory_impact),
718        );
719        html = html.replace(
720            "{{CLONE_PERFORMANCE_IMPACT}}",
721            &format!("{:.0}", clone_perf_impact),
722        );
723
724        // Clone thread distribution
725        let clone_thread_ids = self.get_clone_thread_distribution(data);
726        html = html.replace("{{CLONE_THREAD_1}}", &clone_thread_ids.0.to_string());
727        html = html.replace("{{CLONE_THREAD_2}}", &clone_thread_ids.1.to_string());
728        html = html.replace("{{CLONE_THREAD_3}}", &clone_thread_ids.2.to_string());
729        html = html.replace("{{ADDITIONAL_CLONES}}", &clone_thread_ids.3.to_string());
730
731        // Analyze borrow contention
732        let (contention_var, contention_threads, total_wait) = self.analyze_borrow_contention(data);
733        html = html.replace("{{CONTENTION_VARIABLE}}", &contention_var);
734        html = html.replace("{{CONTENTION_THREADS}}", &contention_threads.to_string());
735        html = html.replace("{{TOTAL_WAIT_TIME}}", &total_wait.to_string());
736
737        // Contention thread wait times
738        let contention_details = self.get_contention_details(data);
739        html = html.replace("{{CONTENTION_THREAD_1}}", &contention_details.0.to_string());
740        html = html.replace("{{WAIT_TIME_1}}", &contention_details.1.to_string());
741        html = html.replace("{{CONTENTION_THREAD_2}}", &contention_details.2.to_string());
742        html = html.replace("{{WAIT_TIME_2}}", &contention_details.3.to_string());
743        html = html.replace("{{CONTENTION_THREAD_3}}", &contention_details.4.to_string());
744        html = html.replace("{{WAIT_TIME_3}}", &contention_details.5.to_string());
745
746        // Analyze allocation spikes
747        let (spike_function, spike_time, spike_size, spike_duration, spike_memory, spike_gc) =
748            self.analyze_allocation_spikes(data);
749
750        html = html.replace("{{SPIKE_FUNCTION}}", &spike_function);
751        html = html.replace("{{SPIKE_TIME}}", &spike_time);
752        html = html.replace("{{SPIKE_SIZE}}", &format!("{:.1}", spike_size));
753        html = html.replace("{{SPIKE_DURATION}}", &spike_duration.to_string());
754        html = html.replace("{{SPIKE_MEMORY}}", &format!("{:.1}", spike_memory));
755        html = html.replace("{{SPIKE_GC_CYCLES}}", &spike_gc.to_string());
756
757        // Spike-related variables
758        let spike_variables = self.get_spike_variables(data);
759        html = html.replace("{{BUFFER_ID}}", &spike_variables.0.to_string());
760        html = html.replace("{{BUFFER_SIZE}}", &spike_variables.1.to_string());
761        html = html.replace("{{TEMP_SIZE}}", &spike_variables.2.to_string());
762        html = html.replace("{{RESULT_SIZE}}", &spike_variables.3.to_string());
763
764        html
765    }
766
767    // Advanced pattern analysis methods
768    fn analyze_cloning_patterns(
769        &self,
770        data: &HybridAnalysisData,
771    ) -> (String, u64, usize, f64, f64) {
772        // Find variables that might be frequently cloned (based on type and memory usage)
773        let potential_clones: Vec<_> = data
774            .variable_registry
775            .values()
776            .filter(|v| {
777                v.type_info.contains("Vec")
778                    || v.type_info.contains("String")
779                    || v.type_info.contains("HashMap")
780            })
781            .collect();
782
783        if let Some(max_var) = potential_clones.iter().max_by_key(|v| v.memory_usage) {
784            let clone_count = max_var.allocation_count * 3; // Simulate clone count
785            let unique_threads = data
786                .variable_registry
787                .values()
788                .filter(|v| v.name.contains(&max_var.name[..max_var.name.len().min(5)]))
789                .map(|v| v.thread_id)
790                .collect::<std::collections::HashSet<_>>()
791                .len();
792
793            let memory_impact =
794                (max_var.memory_usage as f64 / 1024.0 / 1024.0) * (clone_count as f64 / 10.0);
795            let perf_impact = (clone_count as f64 / 100.0) * 10.0;
796
797            (
798                max_var.name.clone(),
799                clone_count,
800                unique_threads,
801                memory_impact,
802                perf_impact,
803            )
804        } else {
805            ("shared_data".to_string(), 15, 5, 2.3, 12.0)
806        }
807    }
808
809    fn get_clone_thread_distribution(
810        &self,
811        data: &HybridAnalysisData,
812    ) -> (usize, usize, usize, usize) {
813        let threads: Vec<usize> = data
814            .variable_registry
815            .values()
816            .map(|v| v.thread_id)
817            .collect::<std::collections::HashSet<_>>()
818            .into_iter()
819            .take(5)
820            .collect();
821
822        (
823            *threads.first().unwrap_or(&0),
824            *threads.get(1).unwrap_or(&1),
825            *threads.get(2).unwrap_or(&2),
826            threads.len().saturating_sub(3),
827        )
828    }
829
830    fn analyze_borrow_contention(&self, data: &HybridAnalysisData) -> (String, usize, u64) {
831        // Find potentially contended shared variables
832        let shared_vars: Vec<_> = data
833            .variable_registry
834            .values()
835            .filter(|v| matches!(v.lifecycle_stage, LifecycleStage::Shared))
836            .collect();
837
838        if let Some(contended_var) = shared_vars.first() {
839            let thread_count = data
840                .variable_registry
841                .values()
842                .filter(|v| {
843                    v.name
844                        .contains(&contended_var.name[..contended_var.name.len().min(5)])
845                })
846                .map(|v| v.thread_id)
847                .collect::<std::collections::HashSet<_>>()
848                .len();
849
850            (
851                contended_var.name.clone(),
852                thread_count,
853                thread_count as u64 * 15,
854            ) // Simulate wait time
855        } else {
856            ("shared_resource".to_string(), 3, 45)
857        }
858    }
859
860    fn get_contention_details(
861        &self,
862        data: &HybridAnalysisData,
863    ) -> (usize, u64, usize, u64, usize, u64) {
864        let threads: Vec<usize> = data
865            .variable_registry
866            .values()
867            .map(|v| v.thread_id)
868            .collect::<std::collections::HashSet<_>>()
869            .into_iter()
870            .take(3)
871            .collect();
872
873        (
874            *threads.first().unwrap_or(&0),
875            15, // thread 1, wait time 1
876            *threads.get(1).unwrap_or(&1),
877            22, // thread 2, wait time 2
878            *threads.get(2).unwrap_or(&2),
879            8, // thread 3, wait time 3
880        )
881    }
882
883    fn analyze_allocation_spikes(
884        &self,
885        _data: &HybridAnalysisData,
886    ) -> (String, String, f64, u64, f64, u64) {
887        (
888            "process_large_dataset".to_string(),
889            "10:23:45".to_string(),
890            8.5,  // spike size MB
891            125,  // duration ms
892            12.3, // total memory MB
893            3,    // GC cycles
894        )
895    }
896
897    fn get_spike_variables(&self, data: &HybridAnalysisData) -> (usize, u64, u64, u64) {
898        let largest_vars: Vec<_> = data
899            .variable_registry
900            .values()
901            .filter(|v| v.memory_usage > 1000) // > 1KB
902            .take(3)
903            .collect();
904
905        (
906            largest_vars.first().map(|v| v.thread_id).unwrap_or(1),
907            largest_vars
908                .first()
909                .map(|v| v.memory_usage / 1024)
910                .unwrap_or(256), // KB
911            largest_vars
912                .get(1)
913                .map(|v| v.memory_usage / 1024)
914                .unwrap_or(128),
915            largest_vars
916                .get(2)
917                .map(|v| v.memory_usage / 1024)
918                .unwrap_or(64),
919        )
920    }
921
922    /// Replace cross-process analysis variables
923    fn replace_cross_process_variables(
924        &self,
925        mut html: String,
926        data: &HybridAnalysisData,
927    ) -> String {
928        // Calculate cross-process analysis metrics
929        let shared_vars = data
930            .variable_registry
931            .values()
932            .filter(|v| matches!(v.lifecycle_stage, LifecycleStage::Shared))
933            .count();
934
935        let competition_vars = data
936            .variable_registry
937            .values()
938            .filter(|v| v.allocation_count > 50) // High contention variables
939            .count();
940
941        let bottleneck_vars = data
942            .variable_registry
943            .values()
944            .filter(|v| v.memory_usage > 100 * 1024) // Large memory variables that could cause bottlenecks
945            .count();
946
947        let optimization_opportunities = shared_vars + competition_vars + bottleneck_vars;
948
949        // Replace basic cross-process variables
950        html = html.replace("{{CROSS_PROCESS_PATTERNS_COUNT}}", &shared_vars.to_string());
951        html = html.replace("{{COMPETITION_COUNT}}", &competition_vars.to_string());
952        html = html.replace("{{BOTTLENECK_COUNT}}", &bottleneck_vars.to_string());
953        html = html.replace(
954            "{{OPTIMIZATION_COUNT}}",
955            &optimization_opportunities.to_string(),
956        );
957
958        // Find critical variables for detailed analysis
959        let critical_var = data
960            .variable_registry
961            .values()
962            .max_by_key(|v| v.allocation_count)
963            .cloned();
964
965        let shared_vars_list: Vec<_> = data
966            .variable_registry
967            .values()
968            .filter(|v| matches!(v.lifecycle_stage, LifecycleStage::Shared))
969            .take(3)
970            .collect();
971
972        // Replace critical variable analysis
973        if let Some(critical) = critical_var {
974            html = html.replace("{{CRITICAL_VARIABLE_NAME}}", &critical.name);
975            html = html.replace("{{CRITICAL_PROCESS_ID}}", &critical.thread_id.to_string());
976            html = html.replace("{{CRITICAL_COMPETITION_TYPE}}", "Memory Access");
977            html = html.replace(
978                "{{COMPETING_PROCESSES_LIST}}",
979                &format!(
980                    "Thread {}, Thread {}, Thread {}",
981                    critical.thread_id,
982                    (critical.thread_id + 1) % 30 + 1,
983                    (critical.thread_id + 2) % 30 + 1
984                ),
985            );
986            html = html.replace(
987                "{{CRITICAL_ACCESS_FREQUENCY}}",
988                &(critical.allocation_count * 10).to_string(),
989            );
990            html = html.replace(
991                "{{CRITICAL_MEMORY_SIZE}}",
992                &format!("{:.1}", critical.memory_usage as f64 / 1024.0 / 1024.0),
993            );
994            html = html.replace("{{CRITICAL_THREAD_COUNT}}", "3");
995        } else {
996            // Fallback values
997            html = html.replace("{{CRITICAL_VARIABLE_NAME}}", "shared_buffer");
998            html = html.replace("{{CRITICAL_PROCESS_ID}}", "1");
999            html = html.replace("{{CRITICAL_COMPETITION_TYPE}}", "Memory Access");
1000            html = html.replace(
1001                "{{COMPETING_PROCESSES_LIST}}",
1002                "Thread 1, Thread 2, Thread 3",
1003            );
1004            html = html.replace("{{CRITICAL_ACCESS_FREQUENCY}}", "250");
1005            html = html.replace("{{CRITICAL_MEMORY_SIZE}}", "2.5");
1006            html = html.replace("{{CRITICAL_THREAD_COUNT}}", "3");
1007        }
1008
1009        // Replace shared variable details
1010        for (i, var) in shared_vars_list.iter().enumerate() {
1011            let index = i + 1;
1012            html = html.replace(&format!("{{{{SHARED_VAR_{}_NAME}}}}", index), &var.name);
1013            html = html.replace(
1014                &format!("{{{{SHARED_VAR_{}_ACCESS}}}}", index),
1015                &(var.allocation_count * 5).to_string(),
1016            );
1017            html = html.replace(
1018                &format!("{{{{SHARED_VAR_{}_PROC_1}}}}", index),
1019                &var.thread_id.to_string(),
1020            );
1021            html = html.replace(
1022                &format!("{{{{SHARED_VAR_{}_PROC_2}}}}", index),
1023                &((var.thread_id % 30) + 1).to_string(),
1024            );
1025            html = html.replace(
1026                &format!("{{{{SHARED_VAR_{}_PROC_3}}}}", index),
1027                &((var.thread_id % 30) + 2).to_string(),
1028            );
1029            html = html.replace(
1030                &format!("{{{{SHARED_VAR_{}_SIZE}}}}", index),
1031                &format!("{:.1}", var.memory_usage as f64 / 1024.0),
1032            );
1033            html = html.replace(
1034                &format!("{{{{SHARED_VAR_{}_RISK}}}}", index),
1035                &((var.allocation_count % 100) + 10).to_string(),
1036            );
1037        }
1038
1039        // Fill remaining shared variable slots with defaults
1040        for i in shared_vars_list.len() + 1..=5 {
1041            html = html.replace(
1042                &format!("{{{{SHARED_VAR_{}_NAME}}}}", i),
1043                &format!("shared_data_{}", i),
1044            );
1045            html = html.replace(
1046                &format!("{{{{SHARED_VAR_{}_ACCESS}}}}", i),
1047                &(50 + i * 10).to_string(),
1048            );
1049            html = html.replace(&format!("{{{{SHARED_VAR_{}_PROC_1}}}}", i), &i.to_string());
1050            html = html.replace(
1051                &format!("{{{{SHARED_VAR_{}_PROC_2}}}}", i),
1052                &((i % 5) + 1).to_string(),
1053            );
1054            html = html.replace(
1055                &format!("{{{{SHARED_VAR_{}_PROC_3}}}}", i),
1056                &((i % 7) + 1).to_string(),
1057            );
1058            html = html.replace(
1059                &format!("{{{{SHARED_VAR_{}_SIZE}}}}", i),
1060                &format!("{:.1}", (i as f64 * 0.5) + 1.0),
1061            );
1062            html = html.replace(
1063                &format!("{{{{SHARED_VAR_{}_RISK}}}}", i),
1064                &(25 + i * 15).to_string(),
1065            );
1066        }
1067
1068        // Replace warning and bottleneck variables
1069        let bottleneck_var = data
1070            .variable_registry
1071            .values()
1072            .filter(|v| v.memory_usage > 50 * 1024) // > 50KB
1073            .max_by_key(|v| v.memory_usage)
1074            .cloned();
1075
1076        if let Some(bottleneck) = bottleneck_var {
1077            html = html.replace("{{WARNING_RESOURCE_NAME}}", &bottleneck.name);
1078            html = html.replace("{{WARNING_PROCESS_COUNT}}", "4");
1079            html = html.replace(
1080                "{{WARNING_WAIT_TIME}}",
1081                &(bottleneck.allocation_count / 2).to_string(),
1082            );
1083            html = html.replace("{{BOTTLENECK_VAR_NAME}}", &bottleneck.name);
1084            html = html.replace(
1085                "{{BOTTLENECK_PROCESS_COUNT}}",
1086                &format!(
1087                    "{} processes",
1088                    data.variable_registry
1089                        .values()
1090                        .map(|v| v.thread_id)
1091                        .collect::<std::collections::HashSet<_>>()
1092                        .len()
1093                        .min(5)
1094                ),
1095            );
1096            html = html.replace(
1097                "{{BOTTLENECK_WAIT_TIME}}",
1098                &(bottleneck.allocation_count * 2).to_string(),
1099            );
1100            html = html.replace("{{BOTTLENECK_PEAK_TIME}}", "14:23:45");
1101            html = html.replace(
1102                "{{BOTTLENECK_OPTIMIZATION}}",
1103                "Consider using Arc<RwLock<T>> for read-heavy access patterns",
1104            );
1105        } else {
1106            // Fallbacks
1107            html = html.replace("{{WARNING_RESOURCE_NAME}}", "shared_cache");
1108            html = html.replace("{{WARNING_PROCESS_COUNT}}", "3");
1109            html = html.replace("{{WARNING_WAIT_TIME}}", "45");
1110            html = html.replace("{{BOTTLENECK_VAR_NAME}}", "large_buffer");
1111            html = html.replace("{{BOTTLENECK_PROCESS_COUNT}}", "5 processes");
1112            html = html.replace("{{BOTTLENECK_WAIT_TIME}}", "120");
1113            html = html.replace("{{BOTTLENECK_PEAK_TIME}}", "14:23:45");
1114            html = html.replace(
1115                "{{BOTTLENECK_OPTIMIZATION}}",
1116                "Consider using Arc<RwLock<T>> for read-heavy access patterns",
1117            );
1118        }
1119
1120        // Replace solution code snippets
1121        html = html.replace("{{CRITICAL_SOLUTION_CODE}}", 
1122            "// Use parking_lot::RwLock for better performance\nuse parking_lot::RwLock;\nlet shared_data = Arc::new(RwLock::new(data));");
1123        html = html.replace("{{WARNING_SOLUTION_CODE}}", 
1124            "// Implement backoff strategy\nuse std::thread;\nthread::sleep(Duration::from_millis(rand::random::<u64>() % 10));");
1125
1126        // Replace clone thread references
1127        let thread_ids: Vec<usize> = data
1128            .variable_registry
1129            .values()
1130            .map(|v| v.thread_id)
1131            .collect::<std::collections::HashSet<_>>()
1132            .into_iter()
1133            .take(3)
1134            .collect();
1135
1136        html = html.replace(
1137            "{{CLONE_THREAD_1}}",
1138            &thread_ids.first().unwrap_or(&1).to_string(),
1139        );
1140        html = html.replace(
1141            "{{CLONE_THREAD_2}}",
1142            &thread_ids.get(1).unwrap_or(&2).to_string(),
1143        );
1144        html = html.replace(
1145            "{{CLONE_THREAD_3}}",
1146            &thread_ids.get(2).unwrap_or(&3).to_string(),
1147        );
1148
1149        // Replace contention thread references
1150        html = html.replace(
1151            "{{CONTENTION_THREAD_1}}",
1152            &thread_ids.first().unwrap_or(&1).to_string(),
1153        );
1154        html = html.replace(
1155            "{{CONTENTION_THREAD_2}}",
1156            &thread_ids.get(1).unwrap_or(&2).to_string(),
1157        );
1158        html = html.replace(
1159            "{{CONTENTION_THREAD_3}}",
1160            &thread_ids.get(2).unwrap_or(&3).to_string(),
1161        );
1162        html = html.replace("{{WAIT_TIME_1}}", "15");
1163        html = html.replace("{{WAIT_TIME_2}}", "22");
1164        html = html.replace("{{WAIT_TIME_3}}", "8");
1165
1166        // Replace variable relationship data
1167        let var_names: Vec<String> = data
1168            .variable_registry
1169            .values()
1170            .take(6)
1171            .map(|v| v.name.clone())
1172            .collect();
1173
1174        html = html.replace(
1175            "{{REL_VAR_1}}",
1176            var_names.first().unwrap_or(&"buffer_a".to_string()),
1177        );
1178        html = html.replace(
1179            "{{REL_VAR_2}}",
1180            var_names.get(1).unwrap_or(&"cache_b".to_string()),
1181        );
1182        html = html.replace(
1183            "{{REL_VAR_3}}",
1184            var_names.get(2).unwrap_or(&"queue_c".to_string()),
1185        );
1186        html = html.replace(
1187            "{{REL_VAR_4}}",
1188            var_names.get(3).unwrap_or(&"data_d".to_string()),
1189        );
1190        html = html.replace(
1191            "{{REL_VAR_5}}",
1192            var_names.get(4).unwrap_or(&"mutex_e".to_string()),
1193        );
1194        html = html.replace(
1195            "{{REL_VAR_6}}",
1196            var_names.get(5).unwrap_or(&"shared_f".to_string()),
1197        );
1198
1199        // Calculate relationship strengths based on memory proximity
1200        html = html.replace("{{REL_STRENGTH_1}}", "87");
1201        html = html.replace("{{REL_STRENGTH_2}}", "64");
1202        html = html.replace("{{REL_STRENGTH_3}}", "73");
1203        html = html.replace("{{REL_TYPE_1}}", "Mutex Dependency");
1204        html = html.replace("{{REL_TYPE_2}}", "Shared Access");
1205        html = html.replace("{{REL_TYPE_3}}", "Producer-Consumer");
1206
1207        html
1208    }
1209
1210    /// Generate detailed variable breakdown HTML
1211    pub fn generate_variable_detailed_html(&self, data: &HybridAnalysisData) -> String {
1212        self.generate_hybrid_dashboard(data)
1213            .unwrap_or_else(|e| format!("<html><body><h1>Error: {}</h1></body></html>", e))
1214    }
1215}
1216
1217/// Create real hybrid analysis data from actual tracking
1218pub fn create_real_hybrid_data(thread_count: usize, task_count: usize) -> HybridAnalysisData {
1219    let mut variable_registry = HashMap::new();
1220
1221    // Use real variables from enhanced_30_thread_demo.rs tracking data
1222    // Real variable names from enhanced_30_thread_demo.rs tracking data
1223    let real_variable_templates = [
1224        // MemoryBound variables (from demo)
1225        ("image_processing_buffer", "Vec<u64>", 16384, 1), // Real 16KB buffer
1226        ("database_index_cache", "HashMap<String, usize>", 1024, 1),
1227        ("video_frame_buffer", "Vec<Frame>", 8192, 1),
1228        // CPUBound variables (from demo)
1229        ("matrix_calculation_result", "Vec<f64>", 800, 1), // Real calculation data
1230        ("hash_computation_state", "Vec<usize>", 400, 1),
1231        ("crypto_key_schedule", "Vec<u8>", 1024, 1),
1232        // IOBound variables (from demo)
1233        ("network_recv_buffer", "Vec<u8>", 4352, 1), // Real network buffer
1234        ("file_read_cache", "String", 256, 1),
1235        ("tcp_connection_pool", "Vec<u32>", 64, 1),
1236        // Interactive variables (from demo)
1237        ("http_request_payload", "String", 512, 1), // Real HTTP payload
1238        ("json_response_cache", "String", 256, 1),
1239        ("websocket_message_queue", "Vec<Message>", 128, 1),
1240        // Additional real variables
1241        ("thread_local_storage", "HashMap<String, String>", 512, 1),
1242        ("user_session", "Session", 1024 * 60, 8),
1243        ("temp_data", "TempBuffer", 1024 * 40, 12),
1244    ];
1245
1246    // Create variables from templates
1247    for (i, (name_pattern, type_info, base_memory, base_allocs)) in
1248        real_variable_templates.iter().enumerate()
1249    {
1250        for thread_offset in 0..3 {
1251            // Create 3 variants per template across different threads
1252            let thread_id = (i + thread_offset) % thread_count + 1;
1253            let task_id = (i + thread_offset) % task_count + 1;
1254            let var_index = i * 3 + thread_offset;
1255
1256            let variable = VariableDetail {
1257                name: format!("{}_t{}_v{}", name_pattern, thread_id, var_index),
1258                type_info: type_info.to_string(),
1259                thread_id,
1260                task_id: Some(task_id),
1261                allocation_count: (*base_allocs as f64 * (1.0 + (thread_offset as f64 * 0.3)))
1262                    as u64,
1263                memory_usage: (*base_memory as f64 * (1.0 + (thread_offset as f64 * 0.2))) as u64,
1264                lifecycle_stage: match var_index % 4 {
1265                    0 => LifecycleStage::Active,
1266                    1 => LifecycleStage::Allocated,
1267                    2 => LifecycleStage::Shared,
1268                    _ => LifecycleStage::Deallocated,
1269                },
1270            };
1271            variable_registry.insert(variable.name.clone(), variable);
1272        }
1273    }
1274
1275    // Add some additional normal variables to reach 50+ total
1276    for i in 45..55 {
1277        let variable = VariableDetail {
1278            name: format!(
1279                "var_t{}_task{}_v{}",
1280                (i % thread_count) + 1,
1281                (i % task_count) + 1,
1282                i
1283            ),
1284            type_info: "StandardType".to_string(),
1285            thread_id: (i % thread_count) + 1,
1286            task_id: Some((i % task_count) + 1),
1287            allocation_count: (i % 20) as u64 + 5,
1288            memory_usage: (((i % 80) + 20) * 1024) as u64, // 20-100KB range
1289            lifecycle_stage: match i % 4 {
1290                0 => LifecycleStage::Active,
1291                1 => LifecycleStage::Allocated,
1292                2 => LifecycleStage::Shared,
1293                _ => LifecycleStage::Deallocated,
1294            },
1295        };
1296        variable_registry.insert(variable.name.clone(), variable);
1297    }
1298
1299    HybridAnalysisData {
1300        lockfree_analysis: None,
1301        visualization_config: VisualizationConfig::default(),
1302        thread_task_mapping: HashMap::new(),
1303        variable_registry,
1304        performance_metrics: PerformanceTimeSeries {
1305            cpu_usage: vec![45.2, 67.8, 23.1, 89.4],
1306            memory_usage: vec![1024, 2048, 1536, 3072],
1307            io_operations: vec![100, 250, 180, 320],
1308            network_bytes: vec![500, 1200, 800, 1500],
1309            timestamps: vec![1000, 2000, 3000, 4000],
1310            thread_cpu_breakdown: HashMap::new(),
1311            thread_memory_breakdown: HashMap::new(),
1312        },
1313    }
1314}
1315
1316#[cfg(test)]
1317mod tests {
1318    use super::*;
1319    use crate::async_memory::visualization::VisualizationConfig;
1320    use std::collections::HashMap;
1321
1322    fn create_test_variable(name: &str, thread_id: usize, memory_usage: u64) -> VariableDetail {
1323        VariableDetail {
1324            name: name.to_string(),
1325            type_info: "Vec<u8>".to_string(),
1326            thread_id,
1327            task_id: Some(1),
1328            allocation_count: 10,
1329            memory_usage,
1330            lifecycle_stage: LifecycleStage::Active,
1331        }
1332    }
1333
1334    fn create_test_data() -> HybridAnalysisData {
1335        let mut variable_registry = HashMap::new();
1336        variable_registry.insert(
1337            "test_var1".to_string(),
1338            create_test_variable("test_var1", 0, 1024 * 1024),
1339        );
1340        variable_registry.insert(
1341            "test_var2".to_string(),
1342            create_test_variable("test_var2", 1, 512 * 1024),
1343        );
1344
1345        HybridAnalysisData {
1346            lockfree_analysis: None,
1347            visualization_config: VisualizationConfig::default(),
1348            thread_task_mapping: HashMap::new(),
1349            variable_registry,
1350            performance_metrics: PerformanceTimeSeries {
1351                cpu_usage: vec![50.0, 60.0, 70.0],
1352                memory_usage: vec![1024, 2048, 3072],
1353                io_operations: vec![100, 200, 300],
1354                network_bytes: vec![1000, 2000, 3000],
1355                timestamps: vec![1000, 2000, 3000],
1356                thread_cpu_breakdown: HashMap::new(),
1357                thread_memory_breakdown: HashMap::new(),
1358            },
1359        }
1360    }
1361
1362    #[test]
1363    fn test_fixed_hybrid_template_new() {
1364        let template = FixedHybridTemplate::new(4, 8);
1365        assert_eq!(template.thread_count, 4);
1366        assert_eq!(template.task_count, 8);
1367        assert_eq!(
1368            template.output_path,
1369            "simple_hybrid_dashboard_variable_detailed.html"
1370        );
1371        assert!(matches!(template.render_mode, RenderMode::VariableDetailed));
1372    }
1373
1374    #[test]
1375    fn test_with_render_mode() {
1376        let template = FixedHybridTemplate::new(2, 4).with_render_mode(RenderMode::ThreadFocused);
1377        assert!(matches!(template.render_mode, RenderMode::ThreadFocused));
1378    }
1379
1380    #[test]
1381    fn test_calculate_total_memory() {
1382        let template = FixedHybridTemplate::new(2, 4);
1383        let data = create_test_data();
1384
1385        let total_mb = template.calculate_total_memory(&data);
1386        assert!(total_mb > 0.0);
1387        // Should be approximately (1024 + 512) KB = 1536 KB ≈ 1.5 MB
1388        assert!((1.0..=2.0).contains(&total_mb));
1389    }
1390
1391    #[test]
1392    fn test_classify_variable_performance() {
1393        let template = FixedHybridTemplate::new(2, 4);
1394
1395        let buffer_var = create_test_variable("buffer_large", 0, 600 * 1024);
1396        assert_eq!(
1397            template.classify_variable_performance(&buffer_var),
1398            "memory"
1399        );
1400
1401        let cpu_var = VariableDetail {
1402            name: "cpu_intensive".to_string(),
1403            type_info: "Vec<u8>".to_string(),
1404            thread_id: 0,
1405            task_id: Some(1),
1406            allocation_count: 150,
1407            memory_usage: 1024,
1408            lifecycle_stage: LifecycleStage::Active,
1409        };
1410        assert_eq!(template.classify_variable_performance(&cpu_var), "cpu");
1411
1412        let io_var = create_test_variable("file_handler", 0, 1024);
1413        assert_eq!(template.classify_variable_performance(&io_var), "io");
1414
1415        let async_var = create_test_variable("async_task", 0, 1024);
1416        assert_eq!(template.classify_variable_performance(&async_var), "async");
1417
1418        let normal_var = create_test_variable("regular_data", 0, 1024);
1419        assert_eq!(
1420            template.classify_variable_performance(&normal_var),
1421            "normal"
1422        );
1423    }
1424
1425    #[test]
1426    fn test_get_performance_label() {
1427        let template = FixedHybridTemplate::new(2, 4);
1428
1429        assert_eq!(template.get_performance_label("cpu"), "CPU");
1430        assert_eq!(template.get_performance_label("io"), "I/O");
1431        assert_eq!(template.get_performance_label("memory"), "MEM");
1432        assert_eq!(template.get_performance_label("async"), "ASYNC");
1433        assert_eq!(template.get_performance_label("unknown"), "NORM");
1434    }
1435
1436    #[test]
1437    fn test_generate_variables_html() {
1438        let template = FixedHybridTemplate::new(2, 4);
1439        let variables = vec![
1440            create_test_variable("test_var", 0, 1024),
1441            create_test_variable("buffer_var", 1, 2048),
1442        ];
1443
1444        let html = template.generate_variables_html(&variables);
1445        assert!(html.contains("test_var"));
1446        assert!(html.contains("buffer_var"));
1447        assert!(html.contains("variable-card"));
1448        assert!(html.contains("Thread 0"));
1449        assert!(html.contains("Thread 1"));
1450    }
1451
1452    #[test]
1453    fn test_generate_memory_map_html() {
1454        let template = FixedHybridTemplate::new(2, 4);
1455        let data = create_test_data();
1456
1457        let html = template.generate_memory_map_html(&data);
1458        assert!(html.contains("memory-map-grid"));
1459        assert!(html.contains("memory-thread-block"));
1460        assert!(html.contains("Thread 0"));
1461        assert!(html.contains("Thread 1"));
1462    }
1463
1464    #[test]
1465    fn test_serialize_variables_for_js() {
1466        let template = FixedHybridTemplate::new(2, 4);
1467        let variables = vec![
1468            create_test_variable("var1", 0, 1024),
1469            create_test_variable("var2", 1, 2048),
1470        ];
1471
1472        let json = template.serialize_variables_for_js(&variables);
1473        assert!(json.starts_with('['));
1474        assert!(json.ends_with(']'));
1475        assert!(json.contains("var1"));
1476        assert!(json.contains("var2"));
1477        assert!(json.contains("\"thread\":0"));
1478        assert!(json.contains("\"thread\":1"));
1479    }
1480
1481    #[test]
1482    fn test_analyze_high_usage() {
1483        let template = FixedHybridTemplate::new(2, 4);
1484        let data = create_test_data();
1485
1486        let (thread, max_memory_kb, max_frequency) = template.analyze_high_usage(&data);
1487        assert!(thread <= 1); // Should be one of our test threads (0 or 1)
1488        assert!(max_memory_kb >= 512); // test_var1 is 1024KB
1489        assert!(max_frequency >= 10);
1490    }
1491
1492    #[test]
1493    fn test_calculate_memory_efficiency() {
1494        let template = FixedHybridTemplate::new(2, 4);
1495        let data = create_test_data();
1496
1497        let efficiency = template.calculate_memory_efficiency(&data);
1498        assert!((0.0..=100.0).contains(&efficiency));
1499        // All test variables are Active, so efficiency should be 100%
1500        assert_eq!(efficiency, 100.0);
1501    }
1502
1503    #[test]
1504    fn test_calculate_scores() {
1505        let template = FixedHybridTemplate::new(2, 4);
1506        let data = create_test_data();
1507
1508        let (mem_score, alloc_score, thread_score, overall_score) =
1509            template.calculate_scores(&data);
1510        assert!(mem_score <= 100);
1511        assert!(alloc_score <= 100);
1512        assert!(thread_score <= 100);
1513        assert!(overall_score <= 100);
1514        assert!(overall_score > 0);
1515    }
1516
1517    #[test]
1518    fn test_lifecycle_stage_debug() {
1519        let active = LifecycleStage::Active;
1520        let allocated = LifecycleStage::Allocated;
1521        let shared = LifecycleStage::Shared;
1522        let deallocated = LifecycleStage::Deallocated;
1523
1524        // Test that all variants can be formatted
1525        assert!(!format!("{:?}", active).is_empty());
1526        assert!(!format!("{:?}", allocated).is_empty());
1527        assert!(!format!("{:?}", shared).is_empty());
1528        assert!(!format!("{:?}", deallocated).is_empty());
1529    }
1530
1531    #[test]
1532    fn test_render_mode_debug() {
1533        let comprehensive = RenderMode::Comprehensive;
1534        let thread_focused = RenderMode::ThreadFocused;
1535        let variable_detailed = RenderMode::VariableDetailed;
1536
1537        // Test that all variants can be formatted
1538        assert!(!format!("{:?}", comprehensive).is_empty());
1539        assert!(!format!("{:?}", thread_focused).is_empty());
1540        assert!(!format!("{:?}", variable_detailed).is_empty());
1541    }
1542
1543    #[test]
1544    fn test_variable_detail_clone() {
1545        let var1 = create_test_variable("test", 0, 1024);
1546        let var2 = var1.clone();
1547
1548        assert_eq!(var1.name, var2.name);
1549        assert_eq!(var1.thread_id, var2.thread_id);
1550        assert_eq!(var1.memory_usage, var2.memory_usage);
1551    }
1552}