1use 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#[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#[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#[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#[derive(Debug, Clone)]
45pub enum LifecycleStage {
46 Allocated,
47 Active,
48 Shared,
49 Deallocated,
50}
51
52#[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 self
85 }
86
87 pub fn with_enhanced_insights(self, _enable: bool) -> Self {
88 self
90 }
91
92 pub fn generate_hybrid_dashboard(
94 &self,
95 data: &HybridAnalysisData,
96 ) -> Result<String, Box<dyn std::error::Error>> {
97 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 fn render_with_template(&self, template: &str, data: &HybridAnalysisData) -> String {
108 let mut html = template.to_string();
109
110 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 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 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 html = self.replace_insights_placeholders(html, data);
148
149 html
150 }
151
152 fn calculate_total_memory(&self, data: &HybridAnalysisData) -> f64 {
154 if let Some(analysis) = &data.lockfree_analysis {
156 analysis.summary.peak_memory_usage as f64 / 1024.0 / 1024.0
157 } else {
158 data.variable_registry
160 .values()
161 .map(|v| v.memory_usage as f64 / 1024.0 / 1024.0)
162 .sum()
163 }
164 }
165
166 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 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 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 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 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 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 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 entry.0 += (variable.memory_usage as usize + 512) / 1024; 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 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 entry.0 += (variable.memory_usage as usize + 512) / 1024; 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 fn replace_insights_placeholders(&self, mut html: String, data: &HybridAnalysisData) -> String {
385 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 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 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 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 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 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 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 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 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 html = self.replace_advanced_pattern_variables(html, data);
476
477 html = self.replace_cross_process_variables(html, data);
479
480 html
481 }
482
483 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) }
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) .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; (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; 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 }
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 } 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; 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 fn replace_advanced_pattern_variables(
704 &self,
705 mut html: String,
706 data: &HybridAnalysisData,
707 ) -> String {
708 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 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 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 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 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 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 fn analyze_cloning_patterns(
769 &self,
770 data: &HybridAnalysisData,
771 ) -> (String, u64, usize, f64, f64) {
772 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; 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 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 ) } 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, *threads.get(1).unwrap_or(&1),
877 22, *threads.get(2).unwrap_or(&2),
879 8, )
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, 125, 12.3, 3, )
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) .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), 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 fn replace_cross_process_variables(
924 &self,
925 mut html: String,
926 data: &HybridAnalysisData,
927 ) -> String {
928 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) .count();
940
941 let bottleneck_vars = data
942 .variable_registry
943 .values()
944 .filter(|v| v.memory_usage > 100 * 1024) .count();
946
947 let optimization_opportunities = shared_vars + competition_vars + bottleneck_vars;
948
949 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 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 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 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 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 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 let bottleneck_var = data
1070 .variable_registry
1071 .values()
1072 .filter(|v| v.memory_usage > 50 * 1024) .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 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 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 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 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 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 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 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
1217pub fn create_real_hybrid_data(thread_count: usize, task_count: usize) -> HybridAnalysisData {
1219 let mut variable_registry = HashMap::new();
1220
1221 let real_variable_templates = [
1224 ("image_processing_buffer", "Vec<u64>", 16384, 1), ("database_index_cache", "HashMap<String, usize>", 1024, 1),
1227 ("video_frame_buffer", "Vec<Frame>", 8192, 1),
1228 ("matrix_calculation_result", "Vec<f64>", 800, 1), ("hash_computation_state", "Vec<usize>", 400, 1),
1231 ("crypto_key_schedule", "Vec<u8>", 1024, 1),
1232 ("network_recv_buffer", "Vec<u8>", 4352, 1), ("file_read_cache", "String", 256, 1),
1235 ("tcp_connection_pool", "Vec<u32>", 64, 1),
1236 ("http_request_payload", "String", 512, 1), ("json_response_cache", "String", 256, 1),
1239 ("websocket_message_queue", "Vec<Message>", 128, 1),
1240 ("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 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 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 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, 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 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); assert!(max_memory_kb >= 512); 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 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 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 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}