1use super::{TaskId, TaskResourceProfile};
7use handlebars::Handlebars;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone)]
13struct TaskTypeMetrics {
14 pub cpu_count: usize,
15 pub cpu_efficiency: f64,
16 pub memory_count: usize,
17 pub memory_efficiency: f64,
18 pub io_count: usize,
19 pub io_efficiency: f64,
20 pub network_count: usize,
21 pub network_efficiency: f64,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct VisualizationConfig {
27 pub title: String,
28 pub theme: Theme,
29 pub include_charts: bool,
30 pub include_baselines: bool,
31 pub include_rankings: bool,
32 pub include_efficiency_breakdown: bool,
33}
34
35impl Default for VisualizationConfig {
36 fn default() -> Self {
37 Self {
38 title: "Async Task Performance Analysis".to_string(),
39 theme: Theme::Dark,
40 include_charts: true,
41 include_baselines: true,
42 include_rankings: true,
43 include_efficiency_breakdown: true,
44 }
45 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub enum Theme {
51 Dark,
52 Light,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct PerformanceBaselines {
58 pub avg_cpu_percent: f64,
59 pub avg_memory_mb: f64,
60 pub avg_io_mbps: f64,
61 pub avg_network_mbps: f64,
62 pub avg_efficiency_score: f64,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct CategoryRanking {
68 pub rank: usize,
69 pub total_in_category: usize,
70 pub category_name: String,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct PerformanceComparison {
76 pub value: f64,
77 pub baseline: f64,
78 pub difference_percent: f64,
79 pub comparison_type: ComparisonType,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub enum ComparisonType {
84 AboveAverage,
85 BelowAverage,
86 NearAverage,
87}
88
89pub struct VisualizationGenerator {
91 config: VisualizationConfig,
92}
93
94impl VisualizationGenerator {
95 pub fn new() -> Self {
97 Self {
98 config: VisualizationConfig::default(),
99 }
100 }
101
102 pub fn with_config(config: VisualizationConfig) -> Self {
104 Self { config }
105 }
106
107 pub fn generate_html_report(
109 &self,
110 profiles: &HashMap<TaskId, TaskResourceProfile>,
111 ) -> Result<String, VisualizationError> {
112 match self.generate_templated_html_report(profiles) {
114 Ok(html) => Ok(html),
115 Err(_) => self.generate_hardcoded_html_report(profiles), }
117 }
118
119 fn get_html_template() -> String {
120 r#"<!DOCTYPE html>
121<html lang="en">
122<head>
123 <meta charset="UTF-8">
124 <meta name="viewport" content="width=device-width, initial-scale=1.0">
125 <title>🦀 {{title}} - Rust Async Performance Dashboard</title>
126 <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
127 <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
128 <style>
129 :root {
130 --rust-orange: #ff6b35;
131 --rust-brown: #8b4513;
132 --async-blue: #4c9aff;
133 --async-cyan: #00d9ff;
134 --async-purple: #8b5cf6;
135 --success-green: #10b981;
136 --warning-yellow: #f59e0b;
137 --error-red: #ef4444;
138 --dark-bg: #0c0c0c;
139 --card-bg: #1a1a1a;
140 --surface-bg: #262626;
141 --text-primary: #ffffff;
142 --text-secondary: #a3a3a3;
143 --border-color: #404040;
144 --glow-rust: rgba(255, 107, 53, 0.3);
145 --glow-async: rgba(76, 154, 255, 0.3);
146 }
147
148 * { margin: 0; padding: 0; box-sizing: border-box; }
149
150 body {
151 font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', monospace;
152 background: linear-gradient(135deg, var(--dark-bg) 0%, #111111 50%, var(--dark-bg) 100%);
153 color: var(--text-primary);
154 line-height: 1.6;
155 min-height: 100vh;
156 overflow-x: hidden;
157 }
158
159 /* Rust-themed animated background */
160 .rust-bg-pattern {
161 position: fixed;
162 top: 0;
163 left: 0;
164 width: 100%;
165 height: 100%;
166 z-index: -1;
167 background-image:
168 radial-gradient(circle at 25% 25%, var(--glow-rust) 0%, transparent 50%),
169 radial-gradient(circle at 75% 75%, var(--glow-async) 0%, transparent 50%),
170 radial-gradient(circle at 50% 10%, rgba(139, 92, 246, 0.1) 0%, transparent 50%);
171 animation: rustFlow 30s ease-in-out infinite;
172 }
173
174 @keyframes rustFlow {
175 0%, 100% { transform: translateY(0px) rotate(0deg); opacity: 0.7; }
176 25% { transform: translateY(-10px) rotate(0.5deg); opacity: 0.9; }
177 50% { transform: translateY(-5px) rotate(-0.5deg); opacity: 0.8; }
178 75% { transform: translateY(-15px) rotate(0.3deg); opacity: 0.6; }
179 }
180
181 .dashboard-container {
182 max-width: 1800px;
183 margin: 0 auto;
184 padding: 20px;
185 position: relative;
186 }
187
188 /* Enhanced Rust-themed header */
189 .rust-header {
190 background: linear-gradient(135deg, var(--rust-orange), var(--rust-brown), var(--async-blue));
191 padding: 3rem 2rem;
192 text-align: center;
193 border-radius: 20px;
194 margin-bottom: 3rem;
195 box-shadow: 0 20px 60px var(--glow-rust), inset 0 1px 0 rgba(255,255,255,0.1);
196 position: relative;
197 overflow: hidden;
198 }
199
200 .rust-header::before {
201 content: '🦀';
202 position: absolute;
203 top: 20px;
204 left: 30px;
205 font-size: 3rem;
206 opacity: 0.3;
207 animation: rustCrab 5s ease-in-out infinite;
208 }
209
210 .rust-header::after {
211 content: '⚡';
212 position: absolute;
213 top: 20px;
214 right: 30px;
215 font-size: 3rem;
216 opacity: 0.3;
217 animation: asyncBolt 3s ease-in-out infinite;
218 }
219
220 @keyframes rustCrab {
221 0%, 100% { transform: translateX(0px) rotate(0deg); }
222 50% { transform: translateX(10px) rotate(5deg); }
223 }
224
225 @keyframes asyncBolt {
226 0%, 100% { transform: scale(1); opacity: 0.3; }
227 50% { transform: scale(1.1); opacity: 0.6; }
228 }
229
230 .rust-title {
231 font-size: 3.5rem;
232 font-weight: 800;
233 margin-bottom: 1rem;
234 background: linear-gradient(45deg, #ffffff, var(--async-cyan), var(--rust-orange));
235 background-clip: text;
236 -webkit-background-clip: text;
237 -webkit-text-fill-color: transparent;
238 text-shadow: 0 0 30px rgba(255, 107, 53, 0.5);
239 position: relative;
240 z-index: 1;
241 }
242
243 .rust-subtitle {
244 font-size: 1.3rem;
245 opacity: 0.9;
246 font-weight: 400;
247 position: relative;
248 z-index: 1;
249 text-shadow: 0 1px 3px rgba(0,0,0,0.5);
250 }
251
252 /* Performance metrics grid */
253 .performance-metrics {
254 display: grid;
255 grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
256 gap: 2rem;
257 margin-bottom: 3rem;
258 }
259
260 .metric-card {
261 background: var(--card-bg);
262 border: 2px solid var(--border-color);
263 border-radius: 16px;
264 padding: 2rem;
265 position: relative;
266 transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
267 overflow: hidden;
268 }
269
270 .metric-card::before {
271 content: '';
272 position: absolute;
273 top: 0;
274 left: 0;
275 right: 0;
276 height: 4px;
277 background: var(--metric-color, linear-gradient(90deg, var(--rust-orange), var(--async-blue)));
278 }
279
280 .metric-card:hover {
281 transform: translateY(-8px) scale(1.02);
282 box-shadow: 0 25px 80px var(--metric-glow, var(--glow-rust));
283 border-color: var(--metric-border, var(--rust-orange));
284 }
285
286 .metric-icon {
287 font-size: 2.8rem;
288 margin-bottom: 1rem;
289 display: block;
290 filter: drop-shadow(0 0 10px currentColor);
291 }
292
293 .metric-value {
294 font-size: 2.8rem;
295 font-weight: 700;
296 margin-bottom: 0.5rem;
297 background: linear-gradient(45deg, var(--metric-color, var(--rust-orange)), var(--async-cyan));
298 background-clip: text;
299 -webkit-background-clip: text;
300 -webkit-text-fill-color: transparent;
301 }
302
303 .metric-label {
304 color: var(--text-secondary);
305 font-size: 1.1rem;
306 text-transform: uppercase;
307 letter-spacing: 0.1em;
308 font-weight: 600;
309 }
310
311 .metric-details {
312 margin-top: 1rem;
313 padding-top: 1rem;
314 border-top: 1px solid var(--border-color);
315 font-size: 0.9rem;
316 color: var(--text-secondary);
317 }
318
319 /* Task type specific colors */
320 .cpu-intensive { --metric-color: #ef4444; --metric-glow: rgba(239, 68, 68, 0.3); --metric-border: #ef4444; }
321 .memory-intensive { --metric-color: #8b5cf6; --metric-glow: rgba(139, 92, 246, 0.3); --metric-border: #8b5cf6; }
322 .io-intensive { --metric-color: #10b981; --metric-glow: rgba(16, 185, 129, 0.3); --metric-border: #10b981; }
323 .network-intensive { --metric-color: #f59e0b; --metric-glow: rgba(245, 158, 11, 0.3); --metric-border: #f59e0b; }
324 .gpu-compute { --metric-color: #ec4899; --metric-glow: rgba(236, 72, 153, 0.3); --metric-border: #ec4899; }
325 .mixed { --metric-color: #6366f1; --metric-glow: rgba(99, 102, 241, 0.3); --metric-border: #6366f1; }
326 .streaming { --metric-color: #14b8a6; --metric-glow: rgba(20, 184, 166, 0.3); --metric-border: #14b8a6; }
327 .background { --metric-color: #64748b; --metric-glow: rgba(100, 116, 139, 0.3); --metric-border: #64748b; }
328
329 /* Task Flow Section */
330 .async-task-flow {
331 background: var(--card-bg);
332 border-radius: 20px;
333 padding: 2rem;
334 margin-bottom: 3rem;
335 border: 1px solid var(--border-color);
336 box-shadow: 0 10px 40px rgba(0,0,0,0.3);
337 }
338
339 .flow-header {
340 display: flex;
341 justify-content: space-between;
342 align-items: center;
343 margin-bottom: 3rem;
344 padding-bottom: 1rem;
345 border-bottom: 2px solid var(--border-color);
346 }
347
348 .flow-title {
349 font-size: 2rem;
350 font-weight: 700;
351 background: linear-gradient(45deg, var(--rust-orange), var(--async-blue));
352 background-clip: text;
353 -webkit-background-clip: text;
354 -webkit-text-fill-color: transparent;
355 }
356
357 .flow-stats {
358 display: flex;
359 gap: 2rem;
360 flex-wrap: wrap;
361 }
362
363 .stat-item {
364 font-size: 0.9rem;
365 color: var(--text-secondary);
366 background: var(--surface-bg);
367 padding: 0.5rem 1rem;
368 border-radius: 8px;
369 border: 1px solid var(--border-color);
370 }
371
372 /* Task Categories */
373 .task-categories {
374 display: grid;
375 grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
376 gap: 2rem;
377 }
378
379 .category-lane {
380 background: var(--surface-bg);
381 border-radius: 16px;
382 padding: 1.5rem;
383 border: 2px solid var(--border-color);
384 transition: all 0.3s ease;
385 }
386
387 .category-lane:hover {
388 border-color: var(--metric-border);
389 box-shadow: 0 8px 32px var(--metric-glow);
390 }
391
392 .lane-header {
393 display: flex;
394 align-items: center;
395 gap: 1rem;
396 margin-bottom: 1.5rem;
397 padding-bottom: 1rem;
398 border-bottom: 1px solid var(--border-color);
399 }
400
401 .lane-icon {
402 font-size: 1.5rem;
403 filter: drop-shadow(0 0 8px currentColor);
404 }
405
406 .lane-title {
407 font-weight: 600;
408 font-size: 1.1rem;
409 flex: 1;
410 }
411
412 .lane-count {
413 background: var(--metric-color);
414 color: var(--dark-bg);
415 padding: 0.25rem 0.75rem;
416 border-radius: 12px;
417 font-weight: 700;
418 font-size: 0.8rem;
419 }
420
421 .lane-efficiency {
422 font-size: 0.8rem;
423 color: var(--text-secondary);
424 }
425
426 /* Task Cards */
427 .tasks-container {
428 display: flex;
429 flex-direction: column;
430 gap: 1rem;
431 max-height: 500px;
432 overflow-y: auto;
433 padding-right: 0.5rem;
434 }
435
436 .tasks-container::-webkit-scrollbar {
437 width: 6px;
438 }
439
440 .tasks-container::-webkit-scrollbar-track {
441 background: var(--border-color);
442 border-radius: 3px;
443 }
444
445 .tasks-container::-webkit-scrollbar-thumb {
446 background: var(--metric-color);
447 border-radius: 3px;
448 }
449
450 .task-card {
451 background: var(--card-bg);
452 border-radius: 12px;
453 padding: 1.5rem;
454 border: 1px solid var(--border-color);
455 border-left: 4px solid var(--metric-color);
456 transition: all 0.3s ease;
457 cursor: pointer;
458 position: relative;
459 overflow: hidden;
460 }
461
462 .task-card::before {
463 content: '';
464 position: absolute;
465 top: 0;
466 left: 0;
467 right: 0;
468 bottom: 0;
469 background: linear-gradient(45deg, transparent 30%, var(--metric-glow) 50%, transparent 70%);
470 opacity: 0;
471 transition: opacity 0.3s ease;
472 }
473
474 .task-card:hover::before {
475 opacity: 1;
476 }
477
478 .task-card:hover {
479 transform: translateY(-3px) scale(1.02);
480 box-shadow: 0 12px 40px var(--metric-glow);
481 border-color: var(--metric-color);
482 }
483
484 .task-card.selected {
485 border-color: var(--rust-orange);
486 box-shadow: 0 0 20px var(--glow-rust);
487 transform: scale(1.05);
488 }
489
490 .task-header {
491 margin-bottom: 1rem;
492 }
493
494 .task-name {
495 font-weight: 600;
496 font-size: 1rem;
497 margin-bottom: 0.25rem;
498 color: var(--text-primary);
499 }
500
501 .task-source {
502 font-size: 0.8rem;
503 color: var(--text-secondary);
504 font-family: 'Courier New', monospace;
505 background: var(--surface-bg);
506 padding: 0.25rem 0.5rem;
507 border-radius: 4px;
508 display: inline-block;
509 }
510
511 .task-metrics {
512 margin-bottom: 1rem;
513 }
514
515 .metric-row {
516 display: flex;
517 align-items: center;
518 justify-content: space-between;
519 margin-bottom: 0.5rem;
520 font-size: 0.85rem;
521 }
522
523 .metric-label {
524 color: var(--text-secondary);
525 min-width: 80px;
526 }
527
528 .metric-value {
529 font-weight: 600;
530 color: var(--text-primary);
531 min-width: 60px;
532 text-align: right;
533 }
534
535 .metric-bar {
536 flex: 1;
537 height: 4px;
538 background: var(--border-color);
539 border-radius: 2px;
540 margin: 0 0.5rem;
541 overflow: hidden;
542 }
543
544 .metric-fill {
545 height: 100%;
546 background: linear-gradient(90deg, var(--metric-color), color-mix(in srgb, var(--metric-color) 70%, white));
547 border-radius: 2px;
548 transition: width 0.6s ease;
549 }
550
551 .task-status {
552 display: flex;
553 justify-content: space-between;
554 align-items: center;
555 font-size: 0.8rem;
556 }
557
558 .status-badge {
559 padding: 0.25rem 0.5rem;
560 border-radius: 4px;
561 font-weight: 600;
562 text-transform: uppercase;
563 }
564
565 .status-badge.completed {
566 background: rgba(16, 185, 129, 0.2);
567 color: var(--success-green);
568 border: 1px solid var(--success-green);
569 }
570
571 .status-badge.running {
572 background: rgba(76, 154, 255, 0.2);
573 color: var(--async-blue);
574 border: 1px solid var(--async-blue);
575 }
576
577 .status-badge.pending {
578 background: rgba(245, 158, 11, 0.2);
579 color: var(--warning-yellow);
580 border: 1px solid var(--warning-yellow);
581 }
582
583 .status-badge.failed {
584 background: rgba(239, 68, 68, 0.2);
585 color: var(--error-red);
586 border: 1px solid var(--error-red);
587 }
588
589 .duration {
590 color: var(--text-secondary);
591 font-weight: 500;
592 }
593
594 /* Responsive Design */
595 @media (max-width: 1200px) {
596 .task-categories {
597 grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
598 }
599
600 .flow-header {
601 flex-direction: column;
602 gap: 1rem;
603 align-items: flex-start;
604 }
605
606 .flow-stats {
607 gap: 1rem;
608 }
609 }
610
611 @media (max-width: 768px) {
612 .task-categories {
613 grid-template-columns: 1fr;
614 }
615
616 .performance-metrics {
617 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
618 }
619
620 .rust-title {
621 font-size: 2.5rem;
622 }
623
624 .flow-stats {
625 flex-direction: column;
626 gap: 0.5rem;
627 }
628
629 .lane-header {
630 flex-wrap: wrap;
631 gap: 0.5rem;
632 }
633 }
634 </style>
635</head>
636<body>
637 <div class="rust-bg-pattern"></div>
638
639 <div class="dashboard-container">
640 <div class="rust-header">
641 <h1 class="rust-title">🦀 {{title}}</h1>
642 <p class="rust-subtitle">{{subtitle}}</p>
643 </div>
644
645 <div class="performance-metrics">
646 <div class="metric-card">
647 <span class="metric-icon">🚀</span>
648 <div class="metric-value">{{total_tasks}}</div>
649 <div class="metric-label">Async Tasks</div>
650 <div class="metric-details">
651 Active: {{active_tasks}} | Completed: {{completed_tasks}} | Failed: {{failed_tasks}}
652 </div>
653 </div>
654
655 <div class="metric-card cpu-intensive">
656 <span class="metric-icon">🔥</span>
657 <div class="metric-value">{{cpu_usage_avg}}%</div>
658 <div class="metric-label">CPU Usage</div>
659 <div class="metric-details">
660 Peak: {{cpu_usage_peak}}% | Cores: {{cpu_cores}} | Context Switches: {{context_switches}}
661 </div>
662 </div>
663
664 <div class="metric-card memory-intensive">
665 <span class="metric-icon">💾</span>
666 <div class="metric-value">{{total_memory_mb}}MB</div>
667 <div class="metric-label">Memory Usage</div>
668 <div class="metric-details">
669 Peak: {{peak_memory_mb}}MB | Allocations: {{total_allocations}} | Efficiency: {{memory_efficiency}}%
670 </div>
671 </div>
672
673 <div class="metric-card io-intensive">
674 <span class="metric-icon">⚡</span>
675 <div class="metric-value">{{io_throughput}}MB/s</div>
676 <div class="metric-label">I/O Throughput</div>
677 <div class="metric-details">
678 Read: {{total_read_mb}}MB | Write: {{total_write_mb}}MB | Ops: {{total_io_ops}}
679 </div>
680 </div>
681
682 <div class="metric-card network-intensive">
683 <span class="metric-icon">🌐</span>
684 <div class="metric-value">{{network_throughput}}Mbps</div>
685 <div class="metric-label">Network Throughput</div>
686 <div class="metric-details">
687 Sent: {{total_sent_mb}}MB | Received: {{total_received_mb}}MB | Latency: {{avg_latency}}ms
688 </div>
689 </div>
690
691 <div class="metric-card">
692 <span class="metric-icon">⚖️</span>
693 <div class="metric-value">{{efficiency_score}}%</div>
694 <div class="metric-label">Overall Efficiency</div>
695 <div class="metric-details">
696 Balance: {{resource_balance}}% | Bottlenecks: {{bottleneck_count}} | Optimization: {{optimization_potential}}%
697 </div>
698 </div>
699 </div>
700
701 <!-- Task Flow Visualization with Rust Async characteristics -->
702 <div class="async-task-flow">
703 <div class="flow-header">
704 <h2 class="flow-title">🦀 Rust Async Task Flow Analysis</h2>
705 <div class="flow-stats">
706 <span class="stat-item">📊 Futures: {{futures_count}}</span>
707 <span class="stat-item">🔄 Polled: {{total_polls}}</span>
708 <span class="stat-item">⏱️ Avg Poll Time: {{avg_poll_time}}μs</span>
709 <span class="stat-item">🎯 Ready Rate: {{ready_rate}}%</span>
710 </div>
711 </div>
712
713 <!-- Task Type Categories -->
714 <div class="task-categories">
715 <div class="category-lane cpu-intensive">
716 <div class="lane-header">
717 <span class="lane-icon">🔥</span>
718 <span class="lane-title">CPU Intensive Tasks</span>
719 <span class="lane-count">{{cpu_intensive_count}}</span>
720 <span class="lane-efficiency">{{cpu_avg_efficiency}}% efficiency</span>
721 </div>
722 <div class="tasks-container">
723 {{#each cpu_intensive_tasks}}
724 <div class="task-card cpu-intensive" data-task-id="{{task_id}}" onclick="event.stopPropagation(); selectTask('{{task_id}}'); return false;">
725 <div class="task-header">
726 <div class="task-name">{{task_name}}</div>
727 <div class="task-source">{{source_file}}:{{source_line}}</div>
728 </div>
729 <div class="task-metrics">
730 <div class="metric-row">
731 <span class="metric-label">CPU:</span>
732 <span class="metric-value">{{cpu_usage}}%</span>
733 <div class="metric-bar">
734 <div class="metric-fill" style="width: {{cpu_usage}}%"></div>
735 </div>
736 </div>
737 <div class="metric-row">
738 <span class="metric-label">Cycles:</span>
739 <span class="metric-value">{{cpu_cycles}}M</span>
740 </div>
741 <div class="metric-row">
742 <span class="metric-label">Instructions:</span>
743 <span class="metric-value">{{instructions}}M</span>
744 </div>
745 <div class="metric-row">
746 <span class="metric-label">Cache Misses:</span>
747 <span class="metric-value">{{cache_misses}}K</span>
748 </div>
749 </div>
750 <div class="task-status">
751 <span class="status-badge {{status_class}}">{{status}}</span>
752 <span class="duration">{{duration_ms}}ms</span>
753 </div>
754 </div>
755 {{/each}}
756 </div>
757 </div>
758
759 <div class="category-lane memory-intensive">
760 <div class="lane-header">
761 <span class="lane-icon">💾</span>
762 <span class="lane-title">Memory Intensive Tasks</span>
763 <span class="lane-count">{{memory_intensive_count}}</span>
764 <span class="lane-efficiency">{{memory_avg_efficiency}}% efficiency</span>
765 </div>
766 <div class="tasks-container">
767 {{#each memory_intensive_tasks}}
768 <div class="task-card memory-intensive" data-task-id="{{task_id}}" onclick="event.stopPropagation(); selectTask('{{task_id}}'); return false;">
769 <div class="task-header">
770 <div class="task-name">{{task_name}}</div>
771 <div class="task-source">{{source_file}}:{{source_line}}</div>
772 </div>
773 <div class="task-metrics">
774 <div class="metric-row">
775 <span class="metric-label">Allocated:</span>
776 <span class="metric-value">{{allocated_mb}}MB</span>
777 <div class="metric-bar">
778 <div class="metric-fill" style="width: {{memory_usage_percent}}%"></div>
779 </div>
780 </div>
781 <div class="metric-row">
782 <span class="metric-label">Peak:</span>
783 <span class="metric-value">{{peak_memory_mb}}MB</span>
784 </div>
785 <div class="metric-row">
786 <span class="metric-label">Allocations:</span>
787 <span class="metric-value">{{allocation_count}}</span>
788 </div>
789 <div class="metric-row">
790 <span class="metric-label">Fragmentation:</span>
791 <span class="metric-value">{{heap_fragmentation}}%</span>
792 </div>
793 </div>
794 <div class="task-status">
795 <span class="status-badge {{status_class}}">{{status}}</span>
796 <span class="duration">{{duration_ms}}ms</span>
797 </div>
798 </div>
799 {{/each}}
800 </div>
801 </div>
802
803 <div class="category-lane io-intensive">
804 <div class="lane-header">
805 <span class="lane-icon">⚡</span>
806 <span class="lane-title">I/O Intensive Tasks</span>
807 <span class="lane-count">{{io_intensive_count}}</span>
808 <span class="lane-efficiency">{{io_avg_efficiency}}% efficiency</span>
809 </div>
810 <div class="tasks-container">
811 {{#each io_intensive_tasks}}
812 <div class="task-card io-intensive" data-task-id="{{task_id}}" onclick="event.stopPropagation(); selectTask('{{task_id}}'); return false;">
813 <div class="task-header">
814 <div class="task-name">{{task_name}}</div>
815 <div class="task-source">{{source_file}}:{{source_line}}</div>
816 </div>
817 <div class="task-metrics">
818 <div class="metric-row">
819 <span class="metric-label">Read:</span>
820 <span class="metric-value">{{bytes_read_mb}}MB</span>
821 <div class="metric-bar">
822 <div class="metric-fill" style="width: {{io_usage_percent}}%"></div>
823 </div>
824 </div>
825 <div class="metric-row">
826 <span class="metric-label">Write:</span>
827 <span class="metric-value">{{bytes_written_mb}}MB</span>
828 </div>
829 <div class="metric-row">
830 <span class="metric-label">Latency:</span>
831 <span class="metric-value">{{avg_latency_us}}μs</span>
832 </div>
833 <div class="metric-row">
834 <span class="metric-label">Queue Depth:</span>
835 <span class="metric-value">{{queue_depth}}</span>
836 </div>
837 </div>
838 <div class="task-status">
839 <span class="status-badge {{status_class}}">{{status}}</span>
840 <span class="duration">{{duration_ms}}ms</span>
841 </div>
842 </div>
843 {{/each}}
844 </div>
845 </div>
846
847 <div class="category-lane network-intensive">
848 <div class="lane-header">
849 <span class="lane-icon">🌐</span>
850 <span class="lane-title">Network Intensive Tasks</span>
851 <span class="lane-count">{{network_intensive_count}}</span>
852 <span class="lane-efficiency">{{network_avg_efficiency}}% efficiency</span>
853 </div>
854 <div class="tasks-container">
855 {{#each network_intensive_tasks}}
856 <div class="task-card network-intensive" data-task-id="{{task_id}}" onclick="event.stopPropagation(); selectTask('{{task_id}}'); return false;">
857 <div class="task-header">
858 <div class="task-name">{{task_name}}</div>
859 <div class="task-source">{{source_file}}:{{source_line}}</div>
860 </div>
861 <div class="task-metrics">
862 <div class="metric-row">
863 <span class="metric-label">Sent:</span>
864 <span class="metric-value">{{bytes_sent_mb}}MB</span>
865 <div class="metric-bar">
866 <div class="metric-fill" style="width: {{network_usage_percent}}%"></div>
867 </div>
868 </div>
869 <div class="metric-row">
870 <span class="metric-label">Received:</span>
871 <span class="metric-value">{{bytes_received_mb}}MB</span>
872 </div>
873 <div class="metric-row">
874 <span class="metric-label">Connections:</span>
875 <span class="metric-value">{{active_connections}}</span>
876 </div>
877 <div class="metric-row">
878 <span class="metric-label">Latency:</span>
879 <span class="metric-value">{{avg_latency_ms}}ms</span>
880 </div>
881 </div>
882 <div class="task-status">
883 <span class="status-badge {{status_class}}">{{status}}</span>
884 <span class="duration">{{duration_ms}}ms</span>
885 </div>
886 </div>
887 {{/each}}
888 </div>
889 </div>
890 </div>
891 </div>
892
893 <!-- Advanced Analytics Section -->
894 <div class="analytics-section">
895 <div class="analytics-header">
896 <h2 class="analytics-title">🔬 Advanced Rust Async Analytics</h2>
897 </div>
898
899 <div class="analytics-grid">
900 <div class="analytics-card">
901 <h3>🚀 Future Polling Insights</h3>
902 <div class="insight-metrics">
903 <div class="insight-metric">
904 <span class="label">Total Polls:</span>
905 <span class="value">{{total_polls}}</span>
906 </div>
907 <div class="insight-metric">
908 <span class="label">Avg Poll Duration:</span>
909 <span class="value">{{avg_poll_duration}}μs</span>
910 </div>
911 <div class="insight-metric">
912 <span class="label">Ready Immediately:</span>
913 <span class="value">{{immediate_ready_percent}}%</span>
914 </div>
915 <div class="insight-metric">
916 <span class="label">Waker Efficiency:</span>
917 <span class="value">{{waker_efficiency}}%</span>
918 </div>
919 </div>
920 </div>
921
922 <div class="analytics-card">
923 <h3>🧠 Memory Management</h3>
924 <div class="insight-metrics">
925 <div class="insight-metric">
926 <span class="label">Total Allocations:</span>
927 <span class="value">{{total_allocations}}</span>
928 </div>
929 <div class="insight-metric">
930 <span class="label">Peak Allocation Rate:</span>
931 <span class="value">{{peak_alloc_rate}}/s</span>
932 </div>
933 <div class="insight-metric">
934 <span class="label">Memory Fragmentation:</span>
935 <span class="value">{{avg_fragmentation}}%</span>
936 </div>
937 <div class="insight-metric">
938 <span class="label">GC Pressure:</span>
939 <span class="value">{{gc_pressure}}%</span>
940 </div>
941 </div>
942 </div>
943
944 <div class="analytics-card">
945 <h3>⚡ Async Runtime Health</h3>
946 <div class="insight-metrics">
947 <div class="insight-metric">
948 <span class="label">Executor Utilization:</span>
949 <span class="value">{{executor_utilization}}%</span>
950 </div>
951 <div class="insight-metric">
952 <span class="label">Task Queue Length:</span>
953 <span class="value">{{avg_queue_length}}</span>
954 </div>
955 <div class="insight-metric">
956 <span class="label">Blocking Tasks:</span>
957 <span class="value">{{blocking_tasks_count}}</span>
958 </div>
959 <div class="insight-metric">
960 <span class="label">Deadlock Risk:</span>
961 <span class="value">{{deadlock_risk}}%</span>
962 </div>
963 </div>
964 </div>
965 </div>
966 </div>
967 </div>
968
969 <script>
970 // Enhanced JavaScript for Rust Async Dashboard
971
972 let selectedTaskId = null;
973 let animationFrameId = null;
974
975 // Initialize dashboard
976 document.addEventListener('DOMContentLoaded', function() {
977 initializeDashboard();
978 startMetricAnimations();
979 setupTaskInteractions();
980 });
981
982 function initializeDashboard() {
983 console.log('🦀 Rust Async Performance Dashboard Initialized');
984
985 // Add pulse animation to metric cards
986 const metricCards = document.querySelectorAll('.metric-card');
987 metricCards.forEach((card, index) => {
988 setTimeout(() => {
989 card.style.opacity = '0';
990 card.style.transform = 'translateY(20px)';
991 card.style.transition = 'all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
992
993 setTimeout(() => {
994 card.style.opacity = '1';
995 card.style.transform = 'translateY(0)';
996 }, 100);
997 }, index * 100);
998 });
999 }
1000
1001 function selectTask(taskId) {
1002 // Check if event exists to prevent errors
1003 if (typeof event !== 'undefined') {
1004 event.preventDefault();
1005 event.stopPropagation();
1006 }
1007
1008 console.log(`🦀 Selecting Rust async task: ${taskId}`);
1009
1010 // Remove previous selection
1011 document.querySelectorAll('.task-card').forEach(card => {
1012 card.classList.remove('selected');
1013 });
1014
1015 // Add selection to clicked task
1016 const taskCard = document.querySelector(`[data-task-id="${taskId}"]`);
1017 if (taskCard) {
1018 taskCard.classList.add('selected');
1019 selectedTaskId = taskId;
1020
1021 // Show task details with delay to ensure DOM is ready
1022 setTimeout(() => {
1023 showTaskDetails(taskId);
1024 }, 100);
1025
1026 // Scroll task into view
1027 taskCard.scrollIntoView({
1028 behavior: 'smooth',
1029 block: 'center'
1030 });
1031 }
1032 }
1033
1034 function showTaskDetails(taskId) {
1035 // Create or update task details panel
1036 let detailsPanel = document.getElementById('task-details-panel');
1037 if (!detailsPanel) {
1038 detailsPanel = createTaskDetailsPanel();
1039 document.body.appendChild(detailsPanel);
1040 }
1041
1042 // Populate with task data
1043 const taskCard = document.querySelector(`[data-task-id="${taskId}"]`);
1044 if (taskCard) {
1045 const taskName = taskCard.querySelector('.task-name').textContent;
1046 const taskSource = taskCard.querySelector('.task-source').textContent;
1047 const taskStatus = taskCard.querySelector('.status-badge').textContent;
1048 const taskDuration = taskCard.querySelector('.duration').textContent;
1049
1050 // Get task type from card classes
1051 const taskType = Array.from(taskCard.classList).find(cls => cls.includes('-intensive')) || 'unknown';
1052 const taskTypeDisplay = taskType.replace('-intensive', '').replace('-', ' ').toUpperCase();
1053
1054 // Build comprehensive task details
1055 detailsPanel.innerHTML = `
1056 <div class="details-header">
1057 <div class="header-content">
1058 <h3>🦀 Rust Async Task Details</h3>
1059 <div class="task-title-info">
1060 <span class="task-title">${taskName}</span>
1061 <span class="task-type-badge ${taskType}">${taskTypeDisplay} TASK</span>
1062 </div>
1063 </div>
1064 <button onclick="closeTaskDetails()" class="close-btn">✕</button>
1065 </div>
1066 <div class="details-content">
1067 <div class="detail-section">
1068 <h4>📍 Source Location & Context</h4>
1069 <div class="source-info">
1070 <p class="source-code">${taskSource}</p>
1071 <div class="execution-info">
1072 <span class="info-item">Status: <strong class="status-${taskStatus.toLowerCase()}">${taskStatus}</strong></span>
1073 <span class="info-item">Duration: <strong>${taskDuration}</strong></span>
1074 <span class="info-item">Task ID: <strong>${taskId}</strong></span>
1075 </div>
1076 </div>
1077 </div>
1078
1079 <div class="detail-section">
1080 <h4>📊 Detailed Performance Metrics</h4>
1081 <div class="metrics-grid">
1082 ${Array.from(taskCard.querySelectorAll('.metric-row')).map(row => {
1083 const label = row.querySelector('.metric-label').textContent;
1084 const value = row.querySelector('.metric-value').textContent;
1085 const bar = row.querySelector('.metric-fill');
1086 const percentage = bar ? bar.style.width : '0%';
1087
1088 return `<div class="metric-detail-card">
1089 <div class="metric-header">
1090 <span class="metric-name">${label}</span>
1091 <span class="metric-val">${value}</span>
1092 </div>
1093 <div class="metric-bar-container">
1094 <div class="metric-bar-bg">
1095 <div class="metric-bar-fill ${taskType}" style="width: ${percentage}"></div>
1096 </div>
1097 <span class="metric-percentage">${percentage}</span>
1098 </div>
1099 </div>`;
1100 }).join('')}
1101 </div>
1102 </div>
1103
1104 <div class="detail-section">
1105 <h4>🔬 Async Runtime Analysis</h4>
1106 <div class="analysis-grid">
1107 <div class="analysis-card">
1108 <div class="analysis-icon">⚡</div>
1109 <div class="analysis-content">
1110 <h5>Future Polling</h5>
1111 <p>This task represents a Rust Future that gets polled by the async executor. Monitor polling frequency and ready states for optimization.</p>
1112 </div>
1113 </div>
1114 <div class="analysis-card">
1115 <div class="analysis-icon">🧠</div>
1116 <div class="analysis-content">
1117 <h5>Resource Usage</h5>
1118 <p>Track ${taskTypeDisplay.toLowerCase()} resource consumption patterns to identify bottlenecks and optimization opportunities.</p>
1119 </div>
1120 </div>
1121 <div class="analysis-card">
1122 <div class="analysis-icon">🎯</div>
1123 <div class="analysis-content">
1124 <h5>Efficiency Score</h5>
1125 <p>Overall task efficiency based on resource utilization, context switches, and async runtime behavior.</p>
1126 </div>
1127 </div>
1128 </div>
1129 </div>
1130
1131 <div class="detail-section">
1132 <h4>🔧 ${taskTypeDisplay} Task Optimization Suggestions</h4>
1133 <div class="suggestions-container">
1134 ${getTaskSpecificSuggestions(taskType)}
1135 </div>
1136 </div>
1137
1138 <div class="detail-section">
1139 <h4>📈 Performance Timeline</h4>
1140 <div class="timeline-placeholder">
1141 <p>📊 Detailed performance timeline visualization would appear here in a full implementation</p>
1142 <div class="timeline-mock">
1143 <div class="timeline-item">Start → Resource Allocation → Execution → Completion</div>
1144 </div>
1145 </div>
1146 </div>
1147 </div>
1148 <div class="details-footer">
1149 <button onclick="exportTaskData('${taskId}')" class="action-btn export-btn">📤 Export Task Data</button>
1150 <button onclick="analyzeTaskPerformance('${taskId}')" class="action-btn analyze-btn">🔍 Deep Analysis</button>
1151 <button onclick="closeTaskDetails()" class="action-btn close-action-btn">Close</button>
1152 </div>
1153 `;
1154
1155 // Show panel with animation
1156 detailsPanel.style.display = 'block';
1157 setTimeout(() => {
1158 detailsPanel.classList.add('visible');
1159 // Add backdrop blur effect
1160 document.body.classList.add('modal-open');
1161 }, 10);
1162
1163 console.log(`🦀 Opened detailed view for Rust async task: ${taskName} (${taskId})`);
1164 }
1165 }
1166
1167 function createTaskDetailsPanel() {
1168 const panel = document.createElement('div');
1169 panel.id = 'task-details-panel';
1170 panel.className = 'task-details-panel';
1171 panel.style.cssText = `
1172 position: fixed;
1173 top: 50%;
1174 left: 50%;
1175 transform: translate(-50%, -50%) scale(0.9);
1176 width: 90%;
1177 max-width: 600px;
1178 max-height: 80vh;
1179 background: var(--card-bg);
1180 border: 2px solid var(--rust-orange);
1181 border-radius: 16px;
1182 box-shadow: 0 20px 60px rgba(0,0,0,0.5);
1183 z-index: 1000;
1184 display: none;
1185 opacity: 0;
1186 transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
1187 overflow-y: auto;
1188 color: var(--text-primary);
1189 `;
1190
1191 return panel;
1192 }
1193
1194 function closeTaskDetails() {
1195 const panel = document.getElementById('task-details-panel');
1196 if (panel) {
1197 panel.classList.remove('visible');
1198 document.body.classList.remove('modal-open');
1199 setTimeout(() => {
1200 panel.style.display = 'none';
1201 }, 300);
1202 }
1203
1204 // Also remove selection from task cards
1205 document.querySelectorAll('.task-card.selected').forEach(card => {
1206 card.classList.remove('selected');
1207 });
1208 selectedTaskId = null;
1209 console.log('🦀 Closed task details panel');
1210 }
1211
1212 // Get task-specific optimization suggestions
1213 function getTaskSpecificSuggestions(taskType) {
1214 const suggestions = {
1215 'cpu-intensive': [
1216 'Use rayon for parallel computation where applicable',
1217 'Consider async-friendly CPU-bound algorithms',
1218 'Implement work-stealing for better load distribution',
1219 'Profile hot paths and optimize critical sections',
1220 'Use tokio::task::yield_now() to prevent blocking the executor'
1221 ],
1222 'memory-intensive': [
1223 'Implement memory pooling to reduce allocations',
1224 'Use async streams for large data processing',
1225 'Consider zero-copy techniques where possible',
1226 'Monitor heap fragmentation and optimize allocation patterns',
1227 'Use Arc and Rc judiciously to minimize cloning'
1228 ],
1229 'io-intensive': [
1230 'Use async I/O operations with proper buffering',
1231 'Implement connection pooling for database operations',
1232 'Consider batch operations to reduce I/O overhead',
1233 'Use tokio::fs for async file operations',
1234 'Optimize buffer sizes based on workload patterns'
1235 ],
1236 'network-intensive': [
1237 'Implement connection reuse and keep-alive',
1238 'Use async HTTP clients with connection pooling',
1239 'Consider implementing backpressure for streaming data',
1240 'Optimize serialization/deserialization performance',
1241 'Use compression for large data transfers'
1242 ]
1243 };
1244
1245 const taskSuggestions = suggestions[taskType] || suggestions['cpu-intensive'];
1246 return taskSuggestions.map(suggestion =>
1247 `<div class="suggestion-item">
1248 <div class="suggestion-icon">💡</div>
1249 <div class="suggestion-text">${suggestion}</div>
1250 </div>`
1251 ).join('');
1252 }
1253
1254 // Export task data functionality
1255 function exportTaskData(taskId) {
1256 const taskCard = document.querySelector(`[data-task-id="${taskId}"]`);
1257 if (taskCard) {
1258 const taskData = {
1259 taskId: taskId,
1260 taskName: taskCard.querySelector('.task-name').textContent,
1261 sourceLocation: taskCard.querySelector('.task-source').textContent,
1262 status: taskCard.querySelector('.status-badge').textContent,
1263 duration: taskCard.querySelector('.duration').textContent,
1264 metrics: Array.from(taskCard.querySelectorAll('.metric-row')).map(row => ({
1265 label: row.querySelector('.metric-label').textContent,
1266 value: row.querySelector('.metric-value').textContent
1267 })),
1268 exportTimestamp: new Date().toISOString()
1269 };
1270
1271 const blob = new Blob([JSON.stringify(taskData, null, 2)], { type: 'application/json' });
1272 const url = URL.createObjectURL(blob);
1273 const a = document.createElement('a');
1274 a.href = url;
1275 a.download = `rust-async-task-${taskId}-${Date.now()}.json`;
1276 a.click();
1277 URL.revokeObjectURL(url);
1278
1279 console.log(`📤 Exported data for task: ${taskId}`);
1280 }
1281 }
1282
1283 // Deep analysis functionality (placeholder)
1284 function analyzeTaskPerformance(taskId) {
1285 console.log(`🔍 Starting deep analysis for task: ${taskId}`);
1286
1287 // Create analysis notification
1288 const notification = document.createElement('div');
1289 notification.className = 'analysis-notification';
1290 notification.innerHTML = `
1291 <div class="notification-content">
1292 <span class="notification-icon">🔬</span>
1293 <span class="notification-text">Deep analysis started for task ${taskId}</span>
1294 <span class="notification-progress">Analyzing...</span>
1295 </div>
1296 `;
1297
1298 document.body.appendChild(notification);
1299
1300 // Simulate analysis process
1301 setTimeout(() => {
1302 notification.querySelector('.notification-progress').textContent = 'Analysis complete!';
1303 notification.classList.add('success');
1304
1305 setTimeout(() => {
1306 document.body.removeChild(notification);
1307 }, 2000);
1308 }, 3000);
1309 }
1310
1311 function startMetricAnimations() {
1312 // Animate metric bars
1313 const metricFills = document.querySelectorAll('.metric-fill');
1314 metricFills.forEach(fill => {
1315 const width = fill.style.width;
1316 fill.style.width = '0%';
1317 setTimeout(() => {
1318 fill.style.width = width;
1319 }, Math.random() * 1000 + 500);
1320 });
1321
1322 // Pulse animation for real-time feeling
1323 setInterval(() => {
1324 const activeCards = document.querySelectorAll('.task-card');
1325 const randomCard = activeCards[Math.floor(Math.random() * activeCards.length)];
1326 if (randomCard && !randomCard.classList.contains('selected')) {
1327 randomCard.style.transform = 'scale(1.01)';
1328 setTimeout(() => {
1329 randomCard.style.transform = '';
1330 }, 200);
1331 }
1332 }, 3000);
1333 }
1334
1335 function setupTaskInteractions() {
1336 // Add keyboard navigation
1337 document.addEventListener('keydown', function(e) {
1338 if (e.key === 'Escape') {
1339 closeTaskDetails();
1340 // Deselect all tasks
1341 document.querySelectorAll('.task-card').forEach(card => {
1342 card.classList.remove('selected');
1343 });
1344 selectedTaskId = null;
1345 }
1346 });
1347
1348 // Add click outside to close details - but only on backdrop, not the panel itself
1349 document.addEventListener('click', function(e) {
1350 const panel = document.getElementById('task-details-panel');
1351 if (panel && panel.classList.contains('visible')) {
1352 // Only close if clicking directly on the backdrop, not the panel content
1353 if (e.target === panel) {
1354 closeTaskDetails();
1355 }
1356 }
1357 });
1358 }
1359
1360 // Utility functions for enhanced interactivity
1361 function filterTasksByType(taskType) {
1362 const allCards = document.querySelectorAll('.task-card');
1363 allCards.forEach(card => {
1364 if (taskType === 'all' || card.classList.contains(taskType)) {
1365 card.style.display = 'block';
1366 card.style.opacity = '1';
1367 } else {
1368 card.style.opacity = '0.3';
1369 }
1370 });
1371 }
1372
1373 function exportDashboardData() {
1374 const data = {
1375 timestamp: new Date().toISOString(),
1376 tasks: Array.from(document.querySelectorAll('.task-card')).map(card => ({
1377 id: card.dataset.taskId,
1378 name: card.querySelector('.task-name').textContent,
1379 type: Array.from(card.classList).find(c => c.includes('-intensive')),
1380 metrics: Array.from(card.querySelectorAll('.metric-row')).map(row => ({
1381 label: row.querySelector('.metric-label').textContent,
1382 value: row.querySelector('.metric-value').textContent
1383 }))
1384 }))
1385 };
1386
1387 const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
1388 const url = URL.createObjectURL(blob);
1389 const a = document.createElement('a');
1390 a.href = url;
1391 a.download = `rust-async-performance-${Date.now()}.json`;
1392 a.click();
1393 URL.revokeObjectURL(url);
1394 }
1395
1396 // Performance monitoring
1397 function monitorPerformance() {
1398 if ('performance' in window) {
1399 const navigation = performance.getEntriesByType('navigation')[0];
1400 console.log(`🦀 Dashboard Load Time: ${navigation.loadEventEnd - navigation.loadEventStart}ms`);
1401
1402 // Monitor memory usage if available
1403 if ('memory' in performance) {
1404 const memory = performance.memory;
1405 console.log(`💾 Memory Usage: ${(memory.usedJSHeapSize / 1048576).toFixed(2)}MB`);
1406 }
1407 }
1408 }
1409
1410 // Initialize performance monitoring
1411 window.addEventListener('load', monitorPerformance);
1412
1413 // CSS for task details panel
1414 const detailsStyles = `
1415 /* Enhanced Task Details Panel */
1416 .task-details-panel {
1417 position: fixed;
1418 top: 50%;
1419 left: 50%;
1420 transform: translate(-50%, -50%) scale(0.9);
1421 width: 95%;
1422 max-width: 900px;
1423 max-height: 90vh;
1424 background: var(--card-bg);
1425 border: 2px solid var(--rust-orange);
1426 border-radius: 20px;
1427 box-shadow: 0 25px 80px rgba(0,0,0,0.7), 0 0 0 1000px rgba(0,0,0,0.5);
1428 z-index: 1000;
1429 display: none;
1430 opacity: 0;
1431 transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
1432 overflow: hidden;
1433 color: var(--text-primary);
1434 }
1435
1436 .task-details-panel.visible {
1437 opacity: 1 !important;
1438 transform: translate(-50%, -50%) scale(1) !important;
1439 }
1440
1441 .body.modal-open {
1442 overflow: hidden;
1443 }
1444
1445 .details-header {
1446 background: linear-gradient(135deg, var(--rust-orange), var(--async-blue));
1447 padding: 2rem;
1448 display: flex;
1449 justify-content: space-between;
1450 align-items: flex-start;
1451 border-bottom: 2px solid var(--border-color);
1452 }
1453
1454 .header-content h3 {
1455 font-size: 1.5rem;
1456 margin-bottom: 1rem;
1457 color: white;
1458 text-shadow: 0 2px 4px rgba(0,0,0,0.3);
1459 }
1460
1461 .task-title-info {
1462 display: flex;
1463 flex-direction: column;
1464 gap: 0.75rem;
1465 }
1466
1467 .task-title {
1468 font-size: 1.3rem;
1469 font-weight: 700;
1470 color: white;
1471 text-shadow: 0 1px 3px rgba(0,0,0,0.5);
1472 }
1473
1474 .task-type-badge {
1475 display: inline-block;
1476 padding: 0.5rem 1rem;
1477 border-radius: 25px;
1478 font-size: 0.8rem;
1479 font-weight: 700;
1480 text-transform: uppercase;
1481 letter-spacing: 0.05em;
1482 background: rgba(255,255,255,0.2);
1483 border: 1px solid rgba(255,255,255,0.3);
1484 color: white;
1485 backdrop-filter: blur(10px);
1486 }
1487
1488 .details-content {
1489 padding: 2rem;
1490 overflow-y: auto;
1491 max-height: 60vh;
1492 }
1493
1494 .details-content::-webkit-scrollbar {
1495 width: 8px;
1496 }
1497
1498 .details-content::-webkit-scrollbar-track {
1499 background: var(--surface-bg);
1500 border-radius: 4px;
1501 }
1502
1503 .details-content::-webkit-scrollbar-thumb {
1504 background: var(--rust-orange);
1505 border-radius: 4px;
1506 }
1507
1508 .detail-section {
1509 margin-bottom: 2.5rem;
1510 padding-bottom: 1.5rem;
1511 border-bottom: 1px solid var(--border-color);
1512 }
1513
1514 .detail-section:last-child {
1515 border-bottom: none;
1516 }
1517
1518 .detail-section h4 {
1519 color: var(--rust-orange);
1520 margin-bottom: 1.5rem;
1521 font-size: 1.2rem;
1522 font-weight: 600;
1523 display: flex;
1524 align-items: center;
1525 gap: 0.5rem;
1526 }
1527
1528 .source-info {
1529 background: var(--surface-bg);
1530 padding: 1.5rem;
1531 border-radius: 12px;
1532 border: 1px solid var(--border-color);
1533 }
1534
1535 .source-code {
1536 background: var(--dark-bg);
1537 padding: 1rem;
1538 border-radius: 8px;
1539 font-family: 'Courier New', monospace;
1540 font-size: 0.9rem;
1541 color: var(--async-cyan);
1542 border-left: 4px solid var(--rust-orange);
1543 margin-bottom: 1rem;
1544 }
1545
1546 .execution-info {
1547 display: flex;
1548 gap: 2rem;
1549 flex-wrap: wrap;
1550 }
1551
1552 .info-item {
1553 font-size: 0.9rem;
1554 color: var(--text-secondary);
1555 }
1556
1557 .info-item strong {
1558 color: var(--text-primary);
1559 font-weight: 600;
1560 }
1561
1562 .metrics-grid {
1563 display: grid;
1564 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1565 gap: 1rem;
1566 }
1567
1568 .metric-detail-card {
1569 background: var(--surface-bg);
1570 padding: 1.5rem;
1571 border-radius: 12px;
1572 border: 1px solid var(--border-color);
1573 transition: all 0.3s ease;
1574 }
1575
1576 .metric-detail-card:hover {
1577 border-color: var(--rust-orange);
1578 box-shadow: 0 4px 16px rgba(255, 107, 53, 0.2);
1579 }
1580
1581 .metric-header {
1582 display: flex;
1583 justify-content: space-between;
1584 align-items: center;
1585 margin-bottom: 1rem;
1586 }
1587
1588 .metric-name {
1589 color: var(--text-secondary);
1590 font-size: 0.9rem;
1591 font-weight: 500;
1592 }
1593
1594 .metric-val {
1595 color: var(--text-primary);
1596 font-weight: 700;
1597 font-size: 1.1rem;
1598 }
1599
1600 .metric-bar-container {
1601 display: flex;
1602 align-items: center;
1603 gap: 1rem;
1604 }
1605
1606 .metric-bar-bg {
1607 flex: 1;
1608 height: 8px;
1609 background: var(--border-color);
1610 border-radius: 4px;
1611 overflow: hidden;
1612 }
1613
1614 .metric-bar-fill {
1615 height: 100%;
1616 border-radius: 4px;
1617 transition: width 0.6s ease;
1618 background: linear-gradient(90deg, var(--metric-color), color-mix(in srgb, var(--metric-color) 70%, white));
1619 }
1620
1621 .metric-percentage {
1622 font-size: 0.8rem;
1623 color: var(--text-secondary);
1624 min-width: 40px;
1625 text-align: right;
1626 }
1627
1628 .analysis-grid {
1629 display: grid;
1630 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
1631 gap: 1.5rem;
1632 }
1633
1634 .analysis-card {
1635 background: var(--surface-bg);
1636 padding: 1.5rem;
1637 border-radius: 12px;
1638 border: 1px solid var(--border-color);
1639 display: flex;
1640 gap: 1rem;
1641 transition: all 0.3s ease;
1642 }
1643
1644 .analysis-card:hover {
1645 border-color: var(--async-blue);
1646 box-shadow: 0 4px 16px rgba(76, 154, 255, 0.2);
1647 }
1648
1649 .analysis-icon {
1650 font-size: 2rem;
1651 filter: drop-shadow(0 0 8px currentColor);
1652 }
1653
1654 .analysis-content h5 {
1655 margin-bottom: 0.5rem;
1656 color: var(--text-primary);
1657 font-weight: 600;
1658 }
1659
1660 .analysis-content p {
1661 font-size: 0.9rem;
1662 color: var(--text-secondary);
1663 line-height: 1.5;
1664 }
1665
1666 .suggestions-container {
1667 display: flex;
1668 flex-direction: column;
1669 gap: 1rem;
1670 }
1671
1672 .suggestion-item {
1673 display: flex;
1674 align-items: flex-start;
1675 gap: 1rem;
1676 background: var(--surface-bg);
1677 padding: 1rem;
1678 border-radius: 8px;
1679 border-left: 4px solid var(--async-blue);
1680 transition: all 0.3s ease;
1681 }
1682
1683 .suggestion-item:hover {
1684 background: color-mix(in srgb, var(--surface-bg) 80%, var(--async-blue));
1685 }
1686
1687 .suggestion-icon {
1688 font-size: 1.2rem;
1689 margin-top: 0.2rem;
1690 }
1691
1692 .suggestion-text {
1693 color: var(--text-primary);
1694 font-size: 0.9rem;
1695 line-height: 1.5;
1696 }
1697
1698 .timeline-placeholder {
1699 background: var(--surface-bg);
1700 padding: 2rem;
1701 border-radius: 12px;
1702 border: 1px dashed var(--border-color);
1703 text-align: center;
1704 }
1705
1706 .timeline-mock {
1707 margin-top: 1rem;
1708 padding: 1rem;
1709 background: var(--dark-bg);
1710 border-radius: 8px;
1711 font-family: monospace;
1712 color: var(--async-cyan);
1713 }
1714
1715 .details-footer {
1716 background: var(--surface-bg);
1717 padding: 1.5rem 2rem;
1718 border-top: 1px solid var(--border-color);
1719 display: flex;
1720 gap: 1rem;
1721 justify-content: flex-end;
1722 flex-wrap: wrap;
1723 }
1724
1725 .action-btn {
1726 padding: 0.75rem 1.5rem;
1727 border: none;
1728 border-radius: 8px;
1729 font-weight: 600;
1730 cursor: pointer;
1731 transition: all 0.3s ease;
1732 display: flex;
1733 align-items: center;
1734 gap: 0.5rem;
1735 font-size: 0.9rem;
1736 }
1737
1738 .export-btn {
1739 background: var(--success-green);
1740 color: white;
1741 }
1742
1743 .export-btn:hover {
1744 background: #059669;
1745 transform: translateY(-2px);
1746 }
1747
1748 .analyze-btn {
1749 background: var(--async-blue);
1750 color: white;
1751 }
1752
1753 .analyze-btn:hover {
1754 background: #2563eb;
1755 transform: translateY(-2px);
1756 }
1757
1758 .close-action-btn {
1759 background: var(--error-red);
1760 color: white;
1761 }
1762
1763 .close-action-btn:hover {
1764 background: #dc2626;
1765 transform: translateY(-2px);
1766 }
1767
1768 .close-btn {
1769 background: rgba(255,255,255,0.2);
1770 color: white;
1771 border: 1px solid rgba(255,255,255,0.3);
1772 padding: 0.75rem 1rem;
1773 border-radius: 8px;
1774 cursor: pointer;
1775 font-weight: 600;
1776 backdrop-filter: blur(10px);
1777 transition: all 0.3s ease;
1778 }
1779
1780 .close-btn:hover {
1781 background: rgba(255,255,255,0.3);
1782 transform: scale(1.05);
1783 }
1784
1785 /* Analysis notification */
1786 .analysis-notification {
1787 position: fixed;
1788 top: 2rem;
1789 right: 2rem;
1790 background: var(--card-bg);
1791 border: 2px solid var(--async-blue);
1792 border-radius: 12px;
1793 padding: 1rem 1.5rem;
1794 box-shadow: 0 8px 32px rgba(0,0,0,0.5);
1795 z-index: 1001;
1796 color: var(--text-primary);
1797 transition: all 0.3s ease;
1798 }
1799
1800 .analysis-notification.success {
1801 border-color: var(--success-green);
1802 }
1803
1804 .notification-content {
1805 display: flex;
1806 align-items: center;
1807 gap: 1rem;
1808 }
1809
1810 .notification-icon {
1811 font-size: 1.5rem;
1812 }
1813
1814 .notification-text {
1815 font-weight: 600;
1816 }
1817
1818 .notification-progress {
1819 color: var(--text-secondary);
1820 font-size: 0.9rem;
1821 }
1822 .analytics-section {
1823 background: var(--card-bg);
1824 border-radius: 20px;
1825 padding: 2rem;
1826 margin-bottom: 3rem;
1827 border: 1px solid var(--border-color);
1828 }
1829 .analytics-title {
1830 font-size: 1.8rem;
1831 font-weight: 700;
1832 margin-bottom: 2rem;
1833 background: linear-gradient(45deg, var(--rust-orange), var(--async-blue));
1834 background-clip: text;
1835 -webkit-background-clip: text;
1836 -webkit-text-fill-color: transparent;
1837 }
1838 .analytics-grid {
1839 display: grid;
1840 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1841 gap: 2rem;
1842 }
1843 .analytics-card {
1844 background: var(--surface-bg);
1845 border-radius: 12px;
1846 padding: 1.5rem;
1847 border: 1px solid var(--border-color);
1848 }
1849 .analytics-card h3 {
1850 margin-bottom: 1rem;
1851 color: var(--text-primary);
1852 }
1853 .insight-metrics {
1854 display: flex;
1855 flex-direction: column;
1856 gap: 0.75rem;
1857 }
1858 .insight-metric {
1859 display: flex;
1860 justify-content: space-between;
1861 padding: 0.5rem;
1862 background: var(--card-bg);
1863 border-radius: 6px;
1864 font-size: 0.9rem;
1865 }
1866 .insight-metric .label {
1867 color: var(--text-secondary);
1868 }
1869 .insight-metric .value {
1870 font-weight: 600;
1871 color: var(--rust-orange);
1872 }
1873 `;
1874
1875 // Inject styles
1876 const styleSheet = document.createElement('style');
1877 styleSheet.textContent = detailsStyles;
1878 document.head.appendChild(styleSheet);
1879
1880 </script>
1881</body>
1882</html>
1883 "#.to_string()
1884 }
1885
1886 fn generate_templated_html_report(
1888 &self,
1889 profiles: &HashMap<TaskId, TaskResourceProfile>,
1890 ) -> Result<String, VisualizationError> {
1891 let template_content = Self::get_html_template();
1892
1893 let mut handlebars = Handlebars::new();
1895 handlebars
1896 .register_template_string("async_dashboard", template_content)
1897 .map_err(|e| {
1898 VisualizationError::TemplateError(format!("Failed to register template: {}", e))
1899 })?;
1900
1901 let template_data = self.build_template_data(profiles)?;
1903
1904 let rendered = handlebars
1906 .render("async_dashboard", &template_data)
1907 .map_err(|e| {
1908 VisualizationError::TemplateError(format!("Failed to render template: {}", e))
1909 })?;
1910
1911 Ok(rendered)
1912 }
1913
1914 fn generate_hardcoded_html_report(
1916 &self,
1917 profiles: &HashMap<TaskId, TaskResourceProfile>,
1918 ) -> Result<String, VisualizationError> {
1919 let analytics = self.analyze_profiles(profiles)?;
1920 self.build_html_report(&analytics, profiles)
1921 }
1922
1923 pub fn analyze_profiles(
1925 &self,
1926 profiles: &HashMap<TaskId, TaskResourceProfile>,
1927 ) -> Result<PerformanceAnalytics, VisualizationError> {
1928 if profiles.is_empty() {
1929 return Err(VisualizationError::NoDataAvailable);
1930 }
1931
1932 let baselines = self.calculate_baselines(profiles);
1933 let rankings = self.calculate_rankings(profiles);
1934 let comparisons = self.calculate_comparisons(profiles, &baselines);
1935
1936 Ok(PerformanceAnalytics {
1937 baselines,
1938 rankings,
1939 comparisons,
1940 total_tasks: profiles.len(),
1941 })
1942 }
1943
1944 fn calculate_baselines(
1946 &self,
1947 profiles: &HashMap<TaskId, TaskResourceProfile>,
1948 ) -> PerformanceBaselines {
1949 let total = profiles.len() as f64;
1950 let mut totals = (0.0, 0.0, 0.0, 0.0, 0.0);
1951
1952 for profile in profiles.values() {
1953 totals.0 += profile.cpu_metrics.usage_percent;
1954 totals.1 += profile.memory_metrics.current_bytes as f64 / 1_048_576.0;
1955 totals.2 += profile.io_metrics.bandwidth_mbps;
1956 totals.3 += profile.network_metrics.throughput_mbps;
1957 totals.4 += profile.efficiency_score;
1958 }
1959
1960 PerformanceBaselines {
1961 avg_cpu_percent: totals.0 / total,
1962 avg_memory_mb: totals.1 / total,
1963 avg_io_mbps: totals.2 / total,
1964 avg_network_mbps: totals.3 / total,
1965 avg_efficiency_score: totals.4 / total,
1966 }
1967 }
1968
1969 fn calculate_rankings(
1971 &self,
1972 profiles: &HashMap<TaskId, TaskResourceProfile>,
1973 ) -> HashMap<TaskId, CategoryRanking> {
1974 let mut rankings = HashMap::new();
1975 let mut category_groups: HashMap<String, Vec<(TaskId, &TaskResourceProfile)>> =
1976 HashMap::new();
1977
1978 for (task_id, profile) in profiles {
1980 let category = format!("{:?}", profile.task_type);
1981 category_groups
1982 .entry(category)
1983 .or_default()
1984 .push((*task_id, profile));
1985 }
1986
1987 for (category_name, mut tasks) in category_groups {
1989 tasks.sort_by(|a, b| {
1990 b.1.efficiency_score
1991 .partial_cmp(&a.1.efficiency_score)
1992 .unwrap_or(std::cmp::Ordering::Equal)
1993 });
1994
1995 let total_in_category = tasks.len();
1996 for (rank, (task_id, _)) in tasks.iter().enumerate() {
1997 rankings.insert(
1998 *task_id,
1999 CategoryRanking {
2000 rank: rank + 1,
2001 total_in_category,
2002 category_name: category_name.clone(),
2003 },
2004 );
2005 }
2006 }
2007
2008 rankings
2009 }
2010
2011 fn calculate_comparisons(
2013 &self,
2014 profiles: &HashMap<TaskId, TaskResourceProfile>,
2015 baselines: &PerformanceBaselines,
2016 ) -> HashMap<TaskId, TaskComparisons> {
2017 let mut comparisons = HashMap::new();
2018
2019 for (task_id, profile) in profiles {
2020 let cpu_comp = self
2021 .compare_to_baseline(profile.cpu_metrics.usage_percent, baselines.avg_cpu_percent);
2022 let memory_comp = self.compare_to_baseline(
2023 profile.memory_metrics.current_bytes as f64 / 1_048_576.0,
2024 baselines.avg_memory_mb,
2025 );
2026 let io_comp =
2027 self.compare_to_baseline(profile.io_metrics.bandwidth_mbps, baselines.avg_io_mbps);
2028 let network_comp = self.compare_to_baseline(
2029 profile.network_metrics.throughput_mbps,
2030 baselines.avg_network_mbps,
2031 );
2032
2033 comparisons.insert(
2034 *task_id,
2035 TaskComparisons {
2036 cpu: cpu_comp,
2037 memory: memory_comp,
2038 io: io_comp,
2039 network: network_comp,
2040 },
2041 );
2042 }
2043
2044 comparisons
2045 }
2046
2047 fn compare_to_baseline(&self, value: f64, baseline: f64) -> PerformanceComparison {
2049 let difference_percent = if baseline != 0.0 {
2050 ((value - baseline) / baseline) * 100.0
2051 } else {
2052 0.0
2053 };
2054
2055 let comparison_type = if difference_percent.abs() < 5.0 {
2056 ComparisonType::NearAverage
2057 } else if difference_percent > 0.0 {
2058 ComparisonType::AboveAverage
2059 } else {
2060 ComparisonType::BelowAverage
2061 };
2062
2063 PerformanceComparison {
2064 value,
2065 baseline,
2066 difference_percent,
2067 comparison_type,
2068 }
2069 }
2070}
2071
2072#[derive(Debug, Clone)]
2074pub struct PerformanceAnalytics {
2075 pub baselines: PerformanceBaselines,
2076 pub rankings: HashMap<TaskId, CategoryRanking>,
2077 pub comparisons: HashMap<TaskId, TaskComparisons>,
2078 pub total_tasks: usize,
2079}
2080
2081#[derive(Debug, Clone)]
2083pub struct TaskComparisons {
2084 pub cpu: PerformanceComparison,
2085 pub memory: PerformanceComparison,
2086 pub io: PerformanceComparison,
2087 pub network: PerformanceComparison,
2088}
2089
2090#[derive(Debug, thiserror::Error)]
2092pub enum VisualizationError {
2093 #[error("No data available for visualization")]
2094 NoDataAvailable,
2095 #[error("Invalid configuration: {0}")]
2096 InvalidConfiguration(String),
2097 #[error("Template generation error: {0}")]
2098 TemplateError(String),
2099 #[error("IO error: {0}")]
2100 IoError(#[from] std::io::Error),
2101}
2102
2103impl Default for VisualizationGenerator {
2104 fn default() -> Self {
2105 Self::new()
2106 }
2107}
2108
2109impl VisualizationGenerator {
2110 fn build_html_report(
2112 &self,
2113 analytics: &PerformanceAnalytics,
2114 profiles: &HashMap<TaskId, TaskResourceProfile>,
2115 ) -> Result<String, VisualizationError> {
2116 let mut html = String::new();
2117
2118 html.push_str(&self.generate_html_header());
2120
2121 html.push_str(&self.generate_summary_section(analytics));
2123
2124 if self.config.include_charts {
2126 html.push_str(&self.generate_charts_section(profiles)?);
2127 }
2128
2129 html.push_str(&self.generate_tasks_section(analytics, profiles)?);
2131
2132 html.push_str(&self.generate_html_footer());
2134
2135 Ok(html)
2136 }
2137
2138 fn generate_html_header(&self) -> String {
2140 let theme_styles = match self.config.theme {
2141 Theme::Dark => self.get_dark_theme_styles(),
2142 Theme::Light => self.get_light_theme_styles(),
2143 };
2144
2145 format!(
2146 r#"<!DOCTYPE html>
2147<html lang="en">
2148<head>
2149 <meta charset="UTF-8">
2150 <meta name="viewport" content="width=device-width, initial-scale=1.0">
2151 <title>{}</title>
2152 <style>
2153 {}
2154 </style>
2155</head>
2156<body>
2157 <div class="container">
2158 <div class="header">
2159 <h1>📊 {}</h1>
2160 <p>Advanced performance analysis with baselines, rankings, and trends</p>
2161 </div>
2162"#,
2163 self.config.title, theme_styles, self.config.title
2164 )
2165 }
2166
2167 fn generate_summary_section(&self, analytics: &PerformanceAnalytics) -> String {
2169 format!(
2170 r#"
2171 <div class="summary">
2172 <div class="summary-card">
2173 <h3>Total Tasks</h3>
2174 <div class="value">{}</div>
2175 </div>
2176 <div class="summary-card">
2177 <h3>Avg CPU Usage</h3>
2178 <div class="value">{:.1}%</div>
2179 </div>
2180 <div class="summary-card">
2181 <h3>Avg Memory</h3>
2182 <div class="value">{:.0}MB</div>
2183 </div>
2184 <div class="summary-card">
2185 <h3>Avg Efficiency</h3>
2186 <div class="value">{:.0}%</div>
2187 </div>
2188 </div>
2189"#,
2190 analytics.total_tasks,
2191 analytics.baselines.avg_cpu_percent,
2192 analytics.baselines.avg_memory_mb,
2193 analytics.baselines.avg_efficiency_score * 100.0
2194 )
2195 }
2196
2197 fn generate_tasks_section(
2199 &self,
2200 analytics: &PerformanceAnalytics,
2201 profiles: &HashMap<TaskId, TaskResourceProfile>,
2202 ) -> Result<String, VisualizationError> {
2203 let mut html = String::new();
2204
2205 html.push_str(
2206 r#"
2207 <div class="tasks-section">
2208 <h2 class="section-title">Task Performance Details</h2>
2209 <div class="tasks-grid">
2210"#,
2211 );
2212
2213 let mut sorted_profiles: Vec<_> = profiles.iter().collect();
2215 sorted_profiles.sort_by(|a, b| {
2216 b.1.efficiency_score
2217 .partial_cmp(&a.1.efficiency_score)
2218 .unwrap_or(std::cmp::Ordering::Equal)
2219 });
2220
2221 for (task_id, profile) in sorted_profiles {
2222 html.push_str(&self.generate_task_card(*task_id, profile, analytics)?);
2223 }
2224
2225 html.push_str(
2226 r#"
2227 </div>
2228 </div>
2229"#,
2230 );
2231
2232 Ok(html)
2233 }
2234
2235 fn generate_task_card(
2237 &self,
2238 task_id: TaskId,
2239 profile: &TaskResourceProfile,
2240 analytics: &PerformanceAnalytics,
2241 ) -> Result<String, VisualizationError> {
2242 let ranking = analytics.rankings.get(&task_id);
2243 let comparisons = analytics.comparisons.get(&task_id);
2244
2245 let task_type_class = format!("{:?}", profile.task_type).to_lowercase();
2246
2247 let rank_info = if let Some(ranking) = ranking {
2248 let rank_class = match ranking.rank {
2249 1 => "rank-1",
2250 2 => "rank-2",
2251 3 => "rank-3",
2252 _ => "",
2253 };
2254 format!(
2255 r#"<div class="ranking-badge {}">#{}/{}</div>"#,
2256 rank_class, ranking.rank, ranking.total_in_category
2257 )
2258 } else {
2259 String::new()
2260 };
2261
2262 if let Some(comp) = comparisons {
2263 self.generate_comparison_info(comp)
2264 } else {
2265 String::new()
2266 };
2267
2268 let efficiency_tooltip = if self.config.include_efficiency_breakdown {
2269 format!(
2270 r#"
2271 <div class="info-icon">?
2272 <div class="tooltip">
2273 <strong>Efficiency Breakdown:</strong><br>
2274 CPU: {:.1}%<br>
2275 Memory: {:.1}%<br>
2276 IO: {:.1}%<br>
2277 Network: {:.1}%<br>
2278 Overall: {:.1}%
2279 </div>
2280 </div>
2281"#,
2282 profile
2283 .efficiency_explanation
2284 .component_scores
2285 .cpu_efficiency
2286 * 100.0,
2287 profile
2288 .efficiency_explanation
2289 .component_scores
2290 .memory_efficiency
2291 * 100.0,
2292 profile
2293 .efficiency_explanation
2294 .component_scores
2295 .io_efficiency
2296 * 100.0,
2297 profile
2298 .efficiency_explanation
2299 .component_scores
2300 .network_efficiency
2301 * 100.0,
2302 profile.efficiency_score * 100.0
2303 )
2304 } else {
2305 String::new()
2306 };
2307
2308 Ok(format!(
2309 r#"
2310 <div class="task-card">
2311 {}
2312 <div class="task-header {}">
2313 <h3 class="task-name">{}</h3>
2314 <span class="task-badge {}">{:?}</span>
2315 </div>
2316 <div class="task-content">
2317 <div class="metrics-grid">
2318 <div class="metric-item">
2319 <div class="metric-label">CPU Usage</div>
2320 <div class="metric-value">{:.1}%</div>
2321 {}
2322 </div>
2323 <div class="metric-item">
2324 <div class="metric-label">Memory</div>
2325 <div class="metric-value">{:.0}MB</div>
2326 {}
2327 </div>
2328 <div class="metric-item">
2329 <div class="metric-label">IO Bandwidth</div>
2330 <div class="metric-value">{:.1}MB/s</div>
2331 {}
2332 </div>
2333 <div class="metric-item">
2334 <div class="metric-label">Network</div>
2335 <div class="metric-value">{:.1}Mbps</div>
2336 {}
2337 </div>
2338 </div>
2339
2340 <div class="efficiency-section">
2341 <div class="efficiency-title">
2342 Efficiency Score
2343 {}
2344 </div>
2345 <div class="efficiency-bar">
2346 <div class="efficiency-fill" style="width: {:.1}%"></div>
2347 </div>
2348 <div class="efficiency-score">{:.1}%</div>
2349 </div>
2350
2351 <div class="source-info">
2352 <div class="source-title">Source Location</div>
2353 <div class="source-detail">
2354 <span class="source-label">File:</span>
2355 <span class="source-value">{}</span>
2356 </div>
2357 <div class="source-detail">
2358 <span class="source-label">Line:</span>
2359 <span class="source-value">{}</span>
2360 </div>
2361 <div class="source-detail">
2362 <span class="source-label">Function:</span>
2363 <span class="source-value">{}</span>
2364 </div>
2365 </div>
2366 </div>
2367 </div>
2368"#,
2369 rank_info,
2370 task_type_class,
2371 profile.task_name,
2372 task_type_class,
2373 profile.task_type,
2374 profile.cpu_metrics.usage_percent,
2375 if let Some(comp) = comparisons {
2376 format!(
2377 "<div class=\"metric-comparison {}\">{}</div>",
2378 self.get_comparison_class(&comp.cpu),
2379 self.format_comparison(&comp.cpu)
2380 )
2381 } else {
2382 String::new()
2383 },
2384 profile.memory_metrics.current_bytes as f64 / 1_048_576.0,
2385 if let Some(comp) = comparisons {
2386 format!(
2387 "<div class=\"metric-comparison {}\">{}</div>",
2388 self.get_comparison_class(&comp.memory),
2389 self.format_comparison(&comp.memory)
2390 )
2391 } else {
2392 String::new()
2393 },
2394 profile.io_metrics.bandwidth_mbps,
2395 if let Some(comp) = comparisons {
2396 format!(
2397 "<div class=\"metric-comparison {}\">{}</div>",
2398 self.get_comparison_class(&comp.io),
2399 self.format_comparison(&comp.io)
2400 )
2401 } else {
2402 String::new()
2403 },
2404 profile.network_metrics.throughput_mbps,
2405 if let Some(comp) = comparisons {
2406 format!(
2407 "<div class=\"metric-comparison {}\">{}</div>",
2408 self.get_comparison_class(&comp.network),
2409 self.format_comparison(&comp.network)
2410 )
2411 } else {
2412 String::new()
2413 },
2414 efficiency_tooltip,
2415 profile.efficiency_score * 100.0,
2416 profile.efficiency_score * 100.0,
2417 profile.source_location.file_path,
2418 profile.source_location.line_number,
2419 profile.source_location.function_name
2420 ))
2421 }
2422
2423 fn generate_comparison_info(&self, _comparisons: &TaskComparisons) -> String {
2425 String::new()
2427 }
2428
2429 fn format_comparison(&self, comparison: &PerformanceComparison) -> String {
2431 match comparison.comparison_type {
2432 ComparisonType::NearAverage => "(≈ avg)".to_string(),
2433 ComparisonType::AboveAverage => {
2434 format!("(+{:.1}% vs avg)", comparison.difference_percent.abs())
2435 }
2436 ComparisonType::BelowAverage => {
2437 format!("(-{:.1}% vs avg)", comparison.difference_percent.abs())
2438 }
2439 }
2440 }
2441
2442 fn get_comparison_class(&self, comparison: &PerformanceComparison) -> &'static str {
2444 match comparison.comparison_type {
2445 ComparisonType::NearAverage => "comparison-average",
2446 ComparisonType::AboveAverage => "comparison-above",
2447 ComparisonType::BelowAverage => "comparison-below",
2448 }
2449 }
2450
2451 fn generate_charts_section(
2453 &self,
2454 profiles: &HashMap<TaskId, TaskResourceProfile>,
2455 ) -> Result<String, VisualizationError> {
2456 let mut html = String::new();
2457
2458 html.push_str(
2459 r#"
2460 <div class="charts-section">
2461 <h2 class="section-title">📈 Performance Trends</h2>
2462"#,
2463 );
2464
2465 let chart_html = self.generate_chart_scripts(profiles)?;
2467 html.push_str(&chart_html);
2468
2469 html.push_str(
2470 r#"
2471 </div>
2472"#,
2473 );
2474
2475 Ok(html)
2476 }
2477
2478 fn build_template_data(
2480 &self,
2481 profiles: &HashMap<TaskId, TaskResourceProfile>,
2482 ) -> Result<serde_json::Value, VisualizationError> {
2483 if profiles.is_empty() {
2484 return Err(VisualizationError::NoDataAvailable);
2485 }
2486
2487 let total_tasks = profiles.len();
2489 let cpu_usage_avg = profiles
2490 .values()
2491 .map(|p| p.cpu_metrics.usage_percent)
2492 .sum::<f64>()
2493 / total_tasks as f64;
2494 let cpu_usage_peak = profiles
2495 .values()
2496 .map(|p| p.cpu_metrics.usage_percent)
2497 .fold(0.0f64, |a, b| a.max(b));
2498 let total_memory_mb = profiles
2499 .values()
2500 .map(|p| p.memory_metrics.allocated_bytes as f64 / 1024.0 / 1024.0)
2501 .sum::<f64>();
2502 let peak_memory_mb = profiles
2503 .values()
2504 .map(|p| p.memory_metrics.peak_bytes as f64 / 1024.0 / 1024.0)
2505 .fold(0.0f64, |a, b| a.max(b));
2506
2507 let total_context_switches = profiles
2509 .values()
2510 .map(|p| p.cpu_metrics.context_switches)
2511 .sum::<u64>();
2512 let total_allocations = profiles
2513 .values()
2514 .map(|p| p.memory_metrics.allocation_count)
2515 .sum::<u64>();
2516 let avg_efficiency =
2517 profiles.values().map(|p| p.efficiency_score).sum::<f64>() / total_tasks as f64;
2518
2519 let total_io_ops = profiles
2521 .values()
2522 .map(|p| p.io_metrics.read_operations + p.io_metrics.write_operations)
2523 .sum::<u64>();
2524 let total_read_mb = profiles
2525 .values()
2526 .map(|p| p.io_metrics.bytes_read as f64 / 1024.0 / 1024.0)
2527 .sum::<f64>();
2528 let total_write_mb = profiles
2529 .values()
2530 .map(|p| p.io_metrics.bytes_written as f64 / 1024.0 / 1024.0)
2531 .sum::<f64>();
2532 let io_throughput = if total_tasks > 0 {
2533 (total_read_mb + total_write_mb) / total_tasks as f64
2534 } else {
2535 0.0
2536 };
2537
2538 let total_sent_mb = profiles
2539 .values()
2540 .map(|p| p.network_metrics.bytes_sent as f64 / 1024.0 / 1024.0)
2541 .sum::<f64>();
2542 let total_received_mb = profiles
2543 .values()
2544 .map(|p| p.network_metrics.bytes_received as f64 / 1024.0 / 1024.0)
2545 .sum::<f64>();
2546 let network_throughput = if total_tasks > 0 {
2547 profiles
2548 .values()
2549 .map(|p| p.network_metrics.throughput_mbps)
2550 .sum::<f64>()
2551 / total_tasks as f64
2552 } else {
2553 0.0
2554 };
2555 let avg_latency = if total_tasks > 0 {
2556 profiles
2557 .values()
2558 .map(|p| p.network_metrics.latency_avg_ms)
2559 .sum::<f64>()
2560 / total_tasks as f64
2561 } else {
2562 0.0
2563 };
2564
2565 let task_type_counts = self.calculate_task_type_metrics(profiles);
2567
2568 let cpu_intensive_tasks =
2570 self.build_task_category_data(profiles, &crate::async_memory::TaskType::CpuIntensive);
2571 let memory_intensive_tasks = self
2572 .build_task_category_data(profiles, &crate::async_memory::TaskType::MemoryIntensive);
2573 let io_intensive_tasks =
2574 self.build_task_category_data(profiles, &crate::async_memory::TaskType::IoIntensive);
2575 let network_intensive_tasks = self
2576 .build_task_category_data(profiles, &crate::async_memory::TaskType::NetworkIntensive);
2577
2578 let futures_count = total_tasks; let total_polls = total_context_switches; let avg_poll_time = if total_polls > 0 {
2582 cpu_usage_avg * 10.0
2583 } else {
2584 0.0
2585 }; let ready_rate = if total_tasks > 0 {
2587 avg_efficiency * 100.0
2588 } else {
2589 0.0
2590 };
2591
2592 let mut template_data = serde_json::Map::new();
2594
2595 template_data.insert(
2597 "title".to_string(),
2598 serde_json::Value::String("Rust Async Performance Analysis".to_string()),
2599 );
2600 template_data.insert("subtitle".to_string(), serde_json::Value::String(format!("Advanced analysis of {} Rust async tasks with detailed performance metrics and Future polling insights", total_tasks)));
2601
2602 template_data.insert(
2604 "total_tasks".to_string(),
2605 serde_json::Value::Number(serde_json::Number::from(total_tasks)),
2606 );
2607 template_data.insert(
2608 "active_tasks".to_string(),
2609 serde_json::Value::Number(serde_json::Number::from(0)),
2610 );
2611 template_data.insert(
2612 "completed_tasks".to_string(),
2613 serde_json::Value::Number(serde_json::Number::from(total_tasks)),
2614 );
2615 template_data.insert(
2616 "failed_tasks".to_string(),
2617 serde_json::Value::Number(serde_json::Number::from(0)),
2618 );
2619
2620 template_data.insert(
2622 "cpu_usage_avg".to_string(),
2623 serde_json::Value::String(format!("{:.1}", cpu_usage_avg)),
2624 );
2625 template_data.insert(
2626 "cpu_usage_peak".to_string(),
2627 serde_json::Value::String(format!("{:.1}", cpu_usage_peak)),
2628 );
2629 template_data.insert(
2630 "cpu_cores".to_string(),
2631 serde_json::Value::Number(serde_json::Number::from(8)),
2632 );
2633 template_data.insert(
2634 "context_switches".to_string(),
2635 serde_json::Value::Number(serde_json::Number::from(total_context_switches)),
2636 );
2637
2638 template_data.insert(
2640 "total_memory_mb".to_string(),
2641 serde_json::Value::String(format!("{:.1}", total_memory_mb)),
2642 );
2643 template_data.insert(
2644 "peak_memory_mb".to_string(),
2645 serde_json::Value::String(format!("{:.1}", peak_memory_mb)),
2646 );
2647 template_data.insert(
2648 "total_allocations".to_string(),
2649 serde_json::Value::Number(serde_json::Number::from(total_allocations)),
2650 );
2651 template_data.insert(
2652 "memory_efficiency".to_string(),
2653 serde_json::Value::String(format!("{:.1}", avg_efficiency * 100.0)),
2654 );
2655
2656 template_data.insert(
2658 "io_throughput".to_string(),
2659 serde_json::Value::String(format!("{:.1}", io_throughput)),
2660 );
2661 template_data.insert(
2662 "total_read_mb".to_string(),
2663 serde_json::Value::String(format!("{:.1}", total_read_mb)),
2664 );
2665 template_data.insert(
2666 "total_write_mb".to_string(),
2667 serde_json::Value::String(format!("{:.1}", total_write_mb)),
2668 );
2669 template_data.insert(
2670 "total_io_ops".to_string(),
2671 serde_json::Value::Number(serde_json::Number::from(total_io_ops)),
2672 );
2673
2674 template_data.insert(
2676 "network_throughput".to_string(),
2677 serde_json::Value::String(format!("{:.1}", network_throughput)),
2678 );
2679 template_data.insert(
2680 "total_sent_mb".to_string(),
2681 serde_json::Value::String(format!("{:.1}", total_sent_mb)),
2682 );
2683 template_data.insert(
2684 "total_received_mb".to_string(),
2685 serde_json::Value::String(format!("{:.1}", total_received_mb)),
2686 );
2687 template_data.insert(
2688 "avg_latency".to_string(),
2689 serde_json::Value::String(format!("{:.1}", avg_latency)),
2690 );
2691
2692 let resource_balance =
2694 profiles.values().map(|p| p.resource_balance).sum::<f64>() / total_tasks as f64 * 100.0;
2695 let bottleneck_count = profiles
2696 .values()
2697 .filter(|p| {
2698 !matches!(
2699 p.bottleneck_type,
2700 crate::async_memory::BottleneckType::Balanced
2701 )
2702 })
2703 .count();
2704
2705 template_data.insert(
2706 "efficiency_score".to_string(),
2707 serde_json::Value::String(format!("{:.1}", avg_efficiency * 100.0)),
2708 );
2709 template_data.insert(
2710 "resource_balance".to_string(),
2711 serde_json::Value::String(format!("{:.1}", resource_balance)),
2712 );
2713 template_data.insert(
2714 "bottleneck_count".to_string(),
2715 serde_json::Value::Number(serde_json::Number::from(bottleneck_count)),
2716 );
2717 template_data.insert(
2718 "optimization_potential".to_string(),
2719 serde_json::Value::String(format!("{:.1}", (1.0 - avg_efficiency) * 100.0)),
2720 );
2721
2722 template_data.insert(
2724 "futures_count".to_string(),
2725 serde_json::Value::Number(serde_json::Number::from(futures_count)),
2726 );
2727 template_data.insert(
2728 "total_polls".to_string(),
2729 serde_json::Value::Number(serde_json::Number::from(total_polls)),
2730 );
2731 template_data.insert(
2732 "avg_poll_time".to_string(),
2733 serde_json::Value::String(format!("{:.1}", avg_poll_time)),
2734 );
2735 template_data.insert(
2736 "ready_rate".to_string(),
2737 serde_json::Value::String(format!("{:.1}", ready_rate)),
2738 );
2739
2740 template_data.insert(
2742 "cpu_intensive_count".to_string(),
2743 serde_json::Value::Number(serde_json::Number::from(task_type_counts.cpu_count)),
2744 );
2745 template_data.insert(
2746 "cpu_avg_efficiency".to_string(),
2747 serde_json::Value::String(format!("{:.1}", task_type_counts.cpu_efficiency)),
2748 );
2749 template_data.insert(
2750 "memory_intensive_count".to_string(),
2751 serde_json::Value::Number(serde_json::Number::from(task_type_counts.memory_count)),
2752 );
2753 template_data.insert(
2754 "memory_avg_efficiency".to_string(),
2755 serde_json::Value::String(format!("{:.1}", task_type_counts.memory_efficiency)),
2756 );
2757 template_data.insert(
2758 "io_intensive_count".to_string(),
2759 serde_json::Value::Number(serde_json::Number::from(task_type_counts.io_count)),
2760 );
2761 template_data.insert(
2762 "io_avg_efficiency".to_string(),
2763 serde_json::Value::String(format!("{:.1}", task_type_counts.io_efficiency)),
2764 );
2765 template_data.insert(
2766 "network_intensive_count".to_string(),
2767 serde_json::Value::Number(serde_json::Number::from(task_type_counts.network_count)),
2768 );
2769 template_data.insert(
2770 "network_avg_efficiency".to_string(),
2771 serde_json::Value::String(format!("{:.1}", task_type_counts.network_efficiency)),
2772 );
2773
2774 template_data.insert(
2776 "cpu_intensive_tasks".to_string(),
2777 serde_json::Value::Array(cpu_intensive_tasks),
2778 );
2779 template_data.insert(
2780 "memory_intensive_tasks".to_string(),
2781 serde_json::Value::Array(memory_intensive_tasks),
2782 );
2783 template_data.insert(
2784 "io_intensive_tasks".to_string(),
2785 serde_json::Value::Array(io_intensive_tasks),
2786 );
2787 template_data.insert(
2788 "network_intensive_tasks".to_string(),
2789 serde_json::Value::Array(network_intensive_tasks),
2790 );
2791
2792 let avg_fragmentation = profiles
2794 .values()
2795 .map(|p| p.memory_metrics.heap_fragmentation)
2796 .sum::<f64>()
2797 / total_tasks as f64
2798 * 100.0;
2799 let blocking_tasks_count = profiles
2800 .values()
2801 .filter(|p| p.cpu_metrics.usage_percent > 80.0)
2802 .count();
2803
2804 template_data.insert(
2805 "avg_poll_duration".to_string(),
2806 serde_json::Value::String(format!("{:.1}", avg_poll_time)),
2807 );
2808 template_data.insert(
2809 "immediate_ready_percent".to_string(),
2810 serde_json::Value::String(format!("{:.1}", ready_rate * 0.8)),
2811 );
2812 template_data.insert(
2813 "waker_efficiency".to_string(),
2814 serde_json::Value::String(format!("{:.1}", avg_efficiency * 95.0)),
2815 );
2816 template_data.insert(
2817 "peak_alloc_rate".to_string(),
2818 serde_json::Value::String(format!("{}", total_allocations / total_tasks as u64)),
2819 );
2820 template_data.insert(
2821 "avg_fragmentation".to_string(),
2822 serde_json::Value::String(format!("{:.1}", avg_fragmentation)),
2823 );
2824 template_data.insert(
2825 "gc_pressure".to_string(),
2826 serde_json::Value::String(format!("{:.1}", (1.0 - avg_efficiency) * 50.0)),
2827 );
2828 template_data.insert(
2829 "executor_utilization".to_string(),
2830 serde_json::Value::String(format!("{:.1}", cpu_usage_avg * 0.8)),
2831 );
2832 template_data.insert(
2833 "avg_queue_length".to_string(),
2834 serde_json::Value::String(format!("{:.1}", total_tasks as f64 * 0.1)),
2835 );
2836 template_data.insert(
2837 "blocking_tasks_count".to_string(),
2838 serde_json::Value::Number(serde_json::Number::from(blocking_tasks_count)),
2839 );
2840 template_data.insert(
2841 "deadlock_risk".to_string(),
2842 serde_json::Value::String(format!(
2843 "{:.1}",
2844 if total_tasks > 10 {
2845 total_tasks as f64 * 0.05
2846 } else {
2847 0.0
2848 }
2849 )),
2850 );
2851
2852 let template_data = serde_json::Value::Object(template_data);
2853
2854 Ok(template_data)
2855 }
2856
2857 fn calculate_task_type_metrics(
2859 &self,
2860 profiles: &HashMap<TaskId, TaskResourceProfile>,
2861 ) -> TaskTypeMetrics {
2862 let mut cpu_count = 0;
2863 let mut cpu_efficiency_sum = 0.0;
2864 let mut memory_count = 0;
2865 let mut memory_efficiency_sum = 0.0;
2866 let mut io_count = 0;
2867 let mut io_efficiency_sum = 0.0;
2868 let mut network_count = 0;
2869 let mut network_efficiency_sum = 0.0;
2870
2871 for profile in profiles.values() {
2872 match profile.task_type {
2873 crate::async_memory::TaskType::CpuIntensive => {
2874 cpu_count += 1;
2875 cpu_efficiency_sum += profile.efficiency_score;
2876 }
2877 crate::async_memory::TaskType::MemoryIntensive => {
2878 memory_count += 1;
2879 memory_efficiency_sum += profile.efficiency_score;
2880 }
2881 crate::async_memory::TaskType::IoIntensive => {
2882 io_count += 1;
2883 io_efficiency_sum += profile.efficiency_score;
2884 }
2885 crate::async_memory::TaskType::NetworkIntensive => {
2886 network_count += 1;
2887 network_efficiency_sum += profile.efficiency_score;
2888 }
2889 _ => {} }
2891 }
2892
2893 TaskTypeMetrics {
2894 cpu_count,
2895 cpu_efficiency: if cpu_count > 0 {
2896 cpu_efficiency_sum / cpu_count as f64 * 100.0
2897 } else {
2898 0.0
2899 },
2900 memory_count,
2901 memory_efficiency: if memory_count > 0 {
2902 memory_efficiency_sum / memory_count as f64 * 100.0
2903 } else {
2904 0.0
2905 },
2906 io_count,
2907 io_efficiency: if io_count > 0 {
2908 io_efficiency_sum / io_count as f64 * 100.0
2909 } else {
2910 0.0
2911 },
2912 network_count,
2913 network_efficiency: if network_count > 0 {
2914 network_efficiency_sum / network_count as f64 * 100.0
2915 } else {
2916 0.0
2917 },
2918 }
2919 }
2920
2921 fn build_task_category_data(
2923 &self,
2924 profiles: &HashMap<TaskId, TaskResourceProfile>,
2925 task_type: &crate::async_memory::TaskType,
2926 ) -> Vec<serde_json::Value> {
2927 profiles
2928 .iter()
2929 .filter(|(_, profile)| {
2930 std::mem::discriminant(&profile.task_type) == std::mem::discriminant(task_type)
2931 })
2932 .map(|(task_id, profile)| {
2933 let mut task_data = serde_json::Map::new();
2934
2935 task_data.insert(
2937 "task_id".to_string(),
2938 serde_json::Value::String(task_id.to_string()),
2939 );
2940 task_data.insert(
2941 "task_name".to_string(),
2942 serde_json::Value::String(profile.task_name.clone()),
2943 );
2944 task_data.insert(
2945 "source_file".to_string(),
2946 serde_json::Value::String(
2947 profile
2948 .source_location
2949 .file_path
2950 .split('/')
2951 .next_back()
2952 .unwrap_or("unknown.rs")
2953 .to_string(),
2954 ),
2955 );
2956 task_data.insert(
2957 "source_line".to_string(),
2958 serde_json::Value::Number(serde_json::Number::from(
2959 profile.source_location.line_number,
2960 )),
2961 );
2962 task_data.insert(
2963 "status".to_string(),
2964 serde_json::Value::String("completed".to_string()),
2965 );
2966 task_data.insert(
2967 "status_class".to_string(),
2968 serde_json::Value::String("completed".to_string()),
2969 );
2970 task_data.insert(
2971 "duration_ms".to_string(),
2972 serde_json::Value::Number(
2973 serde_json::Number::from_f64(profile.duration_ms.unwrap_or(0.0))
2974 .unwrap_or(serde_json::Number::from(0)),
2975 ),
2976 );
2977
2978 task_data.insert(
2980 "cpu_usage".to_string(),
2981 serde_json::Value::String(format!("{:.1}", profile.cpu_metrics.usage_percent)),
2982 );
2983 task_data.insert(
2984 "cpu_cycles".to_string(),
2985 serde_json::Value::String(format!(
2986 "{:.1}",
2987 profile.cpu_metrics.cpu_cycles as f64 / 1_000_000.0
2988 )),
2989 );
2990 task_data.insert(
2991 "instructions".to_string(),
2992 serde_json::Value::String(format!(
2993 "{:.1}",
2994 profile.cpu_metrics.instructions as f64 / 1_000_000.0
2995 )),
2996 );
2997 task_data.insert(
2998 "cache_misses".to_string(),
2999 serde_json::Value::String(format!(
3000 "{:.1}",
3001 profile.cpu_metrics.cache_misses as f64 / 1_000.0
3002 )),
3003 );
3004
3005 task_data.insert(
3007 "allocated_mb".to_string(),
3008 serde_json::Value::String(format!(
3009 "{:.1}",
3010 profile.memory_metrics.allocated_bytes as f64 / 1024.0 / 1024.0
3011 )),
3012 );
3013 task_data.insert(
3014 "peak_memory_mb".to_string(),
3015 serde_json::Value::String(format!(
3016 "{:.1}",
3017 profile.memory_metrics.peak_bytes as f64 / 1024.0 / 1024.0
3018 )),
3019 );
3020 task_data.insert(
3021 "allocation_count".to_string(),
3022 serde_json::Value::Number(serde_json::Number::from(
3023 profile.memory_metrics.allocation_count,
3024 )),
3025 );
3026 task_data.insert(
3027 "heap_fragmentation".to_string(),
3028 serde_json::Value::String(format!(
3029 "{:.1}",
3030 profile.memory_metrics.heap_fragmentation * 100.0
3031 )),
3032 );
3033 task_data.insert(
3034 "memory_usage_percent".to_string(),
3035 serde_json::Value::String(format!(
3036 "{:.1}",
3037 (profile.memory_metrics.current_bytes as f64
3038 / profile.memory_metrics.allocated_bytes.max(1) as f64)
3039 * 100.0
3040 )),
3041 );
3042
3043 task_data.insert(
3045 "bytes_read_mb".to_string(),
3046 serde_json::Value::String(format!(
3047 "{:.1}",
3048 profile.io_metrics.bytes_read as f64 / 1024.0 / 1024.0
3049 )),
3050 );
3051 task_data.insert(
3052 "bytes_written_mb".to_string(),
3053 serde_json::Value::String(format!(
3054 "{:.1}",
3055 profile.io_metrics.bytes_written as f64 / 1024.0 / 1024.0
3056 )),
3057 );
3058 task_data.insert(
3059 "avg_latency_us".to_string(),
3060 serde_json::Value::String(format!("{:.1}", profile.io_metrics.avg_latency_us)),
3061 );
3062 task_data.insert(
3063 "queue_depth".to_string(),
3064 serde_json::Value::Number(serde_json::Number::from(
3065 profile.io_metrics.queue_depth,
3066 )),
3067 );
3068 task_data.insert(
3069 "io_usage_percent".to_string(),
3070 serde_json::Value::String(format!("{:.1}", profile.io_metrics.io_wait_percent)),
3071 );
3072
3073 task_data.insert(
3075 "bytes_sent_mb".to_string(),
3076 serde_json::Value::String(format!(
3077 "{:.1}",
3078 profile.network_metrics.bytes_sent as f64 / 1024.0 / 1024.0
3079 )),
3080 );
3081 task_data.insert(
3082 "bytes_received_mb".to_string(),
3083 serde_json::Value::String(format!(
3084 "{:.1}",
3085 profile.network_metrics.bytes_received as f64 / 1024.0 / 1024.0
3086 )),
3087 );
3088 task_data.insert(
3089 "active_connections".to_string(),
3090 serde_json::Value::Number(serde_json::Number::from(
3091 profile.network_metrics.connections_active,
3092 )),
3093 );
3094 task_data.insert(
3095 "avg_latency_ms".to_string(),
3096 serde_json::Value::String(format!(
3097 "{:.1}",
3098 profile.network_metrics.latency_avg_ms
3099 )),
3100 );
3101 task_data.insert(
3102 "network_usage_percent".to_string(),
3103 serde_json::Value::String(format!(
3104 "{:.1}",
3105 (profile.network_metrics.throughput_mbps / 100.0).min(100.0)
3106 )),
3107 );
3108
3109 serde_json::Value::Object(task_data)
3110 })
3111 .collect()
3112 }
3113
3114 fn generate_chart_scripts(
3116 &self,
3117 profiles: &HashMap<TaskId, TaskResourceProfile>,
3118 ) -> Result<String, VisualizationError> {
3119 let mut cpu_bars = String::new();
3120 let mut memory_bars = String::new();
3121
3122 let max_cpu = profiles
3124 .values()
3125 .map(|p| p.cpu_metrics.usage_percent)
3126 .fold(0.0, f64::max)
3127 .max(100.0);
3128 let max_memory = profiles
3129 .values()
3130 .map(|p| p.memory_metrics.current_bytes as f64 / 1_048_576.0)
3131 .fold(0.0, f64::max);
3132
3133 for profile in profiles.values() {
3134 let cpu_percent = profile.cpu_metrics.usage_percent;
3135 let memory_mb = profile.memory_metrics.current_bytes as f64 / 1_048_576.0;
3136
3137 let cpu_width = (cpu_percent / max_cpu * 100.0).min(100.0);
3138 let memory_width = if max_memory > 0.0 {
3139 (memory_mb / max_memory * 100.0).min(100.0)
3140 } else {
3141 0.0
3142 };
3143
3144 cpu_bars.push_str(&format!(
3145 r#"
3146 <div class="chart-bar">
3147 <div class="bar-label">{}</div>
3148 <div class="bar-container">
3149 <div class="bar-fill cpu-bar" style="width: {:.1}%"></div>
3150 <div class="bar-value">{:.1}%</div>
3151 </div>
3152 </div>
3153"#,
3154 profile.task_name, cpu_width, cpu_percent
3155 ));
3156
3157 memory_bars.push_str(&format!(
3158 r#"
3159 <div class="chart-bar">
3160 <div class="bar-label">{}</div>
3161 <div class="bar-container">
3162 <div class="bar-fill memory-bar" style="width: {:.1}%"></div>
3163 <div class="bar-value">{:.1}MB</div>
3164 </div>
3165 </div>
3166"#,
3167 profile.task_name, memory_width, memory_mb
3168 ));
3169 }
3170
3171 let mut network_bars = String::new();
3173 let max_network = profiles
3174 .values()
3175 .map(|p| p.network_metrics.throughput_mbps)
3176 .fold(0.0, f64::max);
3177
3178 for profile in profiles.values() {
3179 let network_mbps = profile.network_metrics.throughput_mbps;
3180 let network_width = if max_network > 0.0 {
3181 (network_mbps / max_network * 100.0).min(100.0)
3182 } else {
3183 0.0
3184 };
3185
3186 network_bars.push_str(&format!(
3187 r#"
3188 <div class="chart-bar">
3189 <div class="bar-label">{}</div>
3190 <div class="bar-container">
3191 <div class="bar-fill network-bar" style="width: {:.1}%"></div>
3192 <div class="bar-value">{:.1}Mbps</div>
3193 </div>
3194 </div>
3195"#,
3196 profile.task_name, network_width, network_mbps
3197 ));
3198 }
3199
3200 Ok(format!(
3201 r#"
3202 <div class="simple-charts">
3203 <div class="simple-chart">
3204 <h4>CPU Usage Distribution</h4>
3205 <div class="chart-bars">
3206 {}
3207 </div>
3208 </div>
3209 <div class="simple-chart">
3210 <h4>Memory Usage Distribution</h4>
3211 <div class="chart-bars">
3212 {}
3213 </div>
3214 </div>
3215 <div class="simple-chart">
3216 <h4>Network Throughput Distribution</h4>
3217 <div class="chart-bars">
3218 {}
3219 </div>
3220 </div>
3221 </div>
3222"#,
3223 cpu_bars, memory_bars, network_bars
3224 ))
3225 }
3226
3227 fn generate_html_footer(&self) -> String {
3229 r#"
3230 </div>
3231</body>
3232</html>
3233"#
3234 .to_string()
3235 }
3236
3237 fn get_dark_theme_styles(&self) -> &'static str {
3239 r#"
3240 body {
3241 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
3242 margin: 0;
3243 padding: 20px;
3244 background: #0d1117;
3245 color: #f0f6fc;
3246 line-height: 1.6;
3247 }
3248
3249 .container {
3250 max-width: 1400px;
3251 margin: 0 auto;
3252 background: #161b22;
3253 border: 1px solid #30363d;
3254 border-radius: 12px;
3255 overflow: hidden;
3256 }
3257
3258 .header {
3259 background: linear-gradient(135deg, #58a6ff 0%, #a5a5ff 100%);
3260 padding: 2rem;
3261 text-align: center;
3262 color: white;
3263 }
3264
3265 .header h1 {
3266 margin: 0;
3267 font-size: 2.5rem;
3268 font-weight: 700;
3269 }
3270
3271 .summary {
3272 display: grid;
3273 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
3274 gap: 1.5rem;
3275 padding: 2rem;
3276 background: #21262d;
3277 }
3278
3279 .summary-card {
3280 background: #161b22;
3281 border: 1px solid #30363d;
3282 padding: 1.5rem;
3283 border-radius: 8px;
3284 text-align: center;
3285 }
3286
3287 .summary-card h3 {
3288 margin: 0 0 0.5rem 0;
3289 color: #8b949e;
3290 font-size: 0.9rem;
3291 text-transform: uppercase;
3292 }
3293
3294 .summary-card .value {
3295 font-size: 2rem;
3296 font-weight: 700;
3297 color: #58a6ff;
3298 }
3299
3300 .tasks-section {
3301 padding: 2rem;
3302 }
3303
3304 .section-title {
3305 margin: 0 0 2rem 0;
3306 font-size: 1.5rem;
3307 font-weight: 600;
3308 color: #f0f6fc;
3309 text-align: center;
3310 }
3311
3312 .tasks-grid {
3313 display: grid;
3314 grid-template-columns: repeat(auto-fill, minmax(450px, 1fr));
3315 gap: 1.5rem;
3316 }
3317
3318 .task-card {
3319 background: #21262d;
3320 border: 1px solid #30363d;
3321 border-radius: 10px;
3322 overflow: hidden;
3323 transition: transform 0.2s ease;
3324 position: relative;
3325 }
3326
3327 .task-card:hover {
3328 transform: translateY(-2px);
3329 box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
3330 }
3331
3332 .ranking-badge {
3333 position: absolute;
3334 top: 10px;
3335 right: 10px;
3336 background: linear-gradient(135deg, #f9c513, #ffd700);
3337 color: #000;
3338 padding: 0.25rem 0.5rem;
3339 border-radius: 12px;
3340 font-size: 0.7rem;
3341 font-weight: 700;
3342 z-index: 10;
3343 }
3344
3345 .ranking-badge.rank-1 { background: linear-gradient(135deg, #ffd700, #ffed4e); }
3346 .ranking-badge.rank-2 { background: linear-gradient(135deg, #c0c0c0, #e8e8e8); }
3347 .ranking-badge.rank-3 { background: linear-gradient(135deg, #cd7f32, #daa520); }
3348
3349 .task-header {
3350 padding: 1.5rem;
3351 background: #161b22;
3352 border-bottom: 1px solid #30363d;
3353 position: relative;
3354 }
3355
3356 .task-header::before {
3357 content: '';
3358 position: absolute;
3359 top: 0;
3360 left: 0;
3361 right: 0;
3362 height: 3px;
3363 }
3364
3365 .task-header.cpuintensive::before { background: #f85149; }
3366 .task-header.iointensive::before { background: #58a6ff; }
3367 .task-header.networkintensive::before { background: #3fb950; }
3368 .task-header.memoryintensive::before { background: #a5a5ff; }
3369 .task-header.mixed::before { background: #f9c513; }
3370
3371 .task-name {
3372 margin: 0 0 0.5rem 0;
3373 font-size: 1.125rem;
3374 font-weight: 600;
3375 color: #f0f6fc;
3376 }
3377
3378 .task-badge {
3379 display: inline-block;
3380 padding: 0.25rem 0.75rem;
3381 border-radius: 12px;
3382 font-size: 0.75rem;
3383 font-weight: 600;
3384 text-transform: uppercase;
3385 }
3386
3387 .task-badge.cpuintensive { background: #f85149; color: white; }
3388 .task-badge.iointensive { background: #58a6ff; color: white; }
3389 .task-badge.networkintensive { background: #3fb950; color: white; }
3390 .task-badge.memoryintensive { background: #a5a5ff; color: white; }
3391 .task-badge.mixed { background: #f9c513; color: black; }
3392
3393 .task-content {
3394 padding: 1.5rem;
3395 }
3396
3397 .metrics-grid {
3398 display: grid;
3399 grid-template-columns: 1fr 1fr;
3400 gap: 1rem;
3401 margin-bottom: 1rem;
3402 }
3403
3404 .metric-item {
3405 background: #161b22;
3406 border: 1px solid #30363d;
3407 border-radius: 6px;
3408 padding: 1rem;
3409 text-align: center;
3410 }
3411
3412 .metric-label {
3413 font-size: 0.75rem;
3414 color: #8b949e;
3415 margin-bottom: 0.25rem;
3416 text-transform: uppercase;
3417 }
3418
3419 .metric-value {
3420 font-size: 1.25rem;
3421 font-weight: 700;
3422 color: #f0f6fc;
3423 }
3424
3425 .metric-comparison {
3426 font-size: 0.7rem;
3427 margin-top: 0.25rem;
3428 }
3429
3430 .comparison-above { color: #f85149; }
3431 .comparison-below { color: #3fb950; }
3432 .comparison-average { color: #f9c513; }
3433
3434 .efficiency-section {
3435 background: #161b22;
3436 border: 1px solid #30363d;
3437 border-radius: 6px;
3438 padding: 1rem;
3439 margin-top: 1rem;
3440 }
3441
3442 .efficiency-title {
3443 font-size: 0.875rem;
3444 color: #8b949e;
3445 margin-bottom: 0.5rem;
3446 text-transform: uppercase;
3447 display: flex;
3448 align-items: center;
3449 gap: 0.5rem;
3450 }
3451
3452 .info-icon {
3453 cursor: help;
3454 background: #30363d;
3455 color: #8b949e;
3456 border-radius: 50%;
3457 width: 16px;
3458 height: 16px;
3459 display: flex;
3460 align-items: center;
3461 justify-content: center;
3462 font-size: 0.7rem;
3463 font-weight: bold;
3464 position: relative;
3465 }
3466
3467 .info-icon:hover {
3468 background: #58a6ff;
3469 color: white;
3470 }
3471
3472 .tooltip {
3473 position: absolute;
3474 bottom: 100%;
3475 right: 0;
3476 background: #161b22;
3477 border: 1px solid #30363d;
3478 border-radius: 6px;
3479 padding: 0.75rem;
3480 min-width: 200px;
3481 font-size: 0.8rem;
3482 color: #f0f6fc;
3483 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
3484 z-index: 1000;
3485 opacity: 0;
3486 visibility: hidden;
3487 transition: opacity 0.3s ease;
3488 }
3489
3490 .info-icon:hover .tooltip {
3491 opacity: 1;
3492 visibility: visible;
3493 }
3494
3495 .efficiency-bar {
3496 width: 100%;
3497 height: 8px;
3498 background: #30363d;
3499 border-radius: 4px;
3500 overflow: hidden;
3501 margin-bottom: 0.5rem;
3502 }
3503
3504 .efficiency-fill {
3505 height: 100%;
3506 background: linear-gradient(90deg, #3fb950, #f9c513, #f85149);
3507 border-radius: 4px;
3508 }
3509
3510 .efficiency-score {
3511 font-size: 1rem;
3512 font-weight: 700;
3513 color: #58a6ff;
3514 text-align: right;
3515 }
3516
3517 .source-info {
3518 background: #161b22;
3519 border: 1px solid #30363d;
3520 border-radius: 6px;
3521 padding: 1rem;
3522 margin-top: 1rem;
3523 }
3524
3525 .source-title {
3526 font-size: 0.875rem;
3527 color: #8b949e;
3528 margin-bottom: 0.5rem;
3529 text-transform: uppercase;
3530 }
3531
3532 .source-detail {
3533 display: flex;
3534 justify-content: space-between;
3535 margin-bottom: 0.25rem;
3536 font-size: 0.8rem;
3537 }
3538
3539 .source-label {
3540 color: #8b949e;
3541 }
3542
3543 .source-value {
3544 color: #f0f6fc;
3545 font-family: 'Courier New', monospace;
3546 }
3547
3548 .charts-section {
3549 padding: 2rem;
3550 background: #161b22;
3551 border-top: 1px solid #30363d;
3552 }
3553
3554 .charts-grid {
3555 display: grid;
3556 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
3557 gap: 2rem;
3558 margin-top: 1.5rem;
3559 }
3560
3561 .chart-container {
3562 background: #21262d;
3563 border: 1px solid #30363d;
3564 border-radius: 8px;
3565 padding: 1.5rem;
3566 }
3567
3568 .chart-title {
3569 margin: 0 0 1rem 0;
3570 font-size: 1rem;
3571 font-weight: 600;
3572 color: #f0f6fc;
3573 text-align: center;
3574 }
3575
3576 /* Simple CSS Charts */
3577 .simple-charts {
3578 display: grid;
3579 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
3580 gap: 2rem;
3581 padding: 2rem;
3582 }
3583
3584 .simple-chart {
3585 background: #21262d;
3586 border: 1px solid #30363d;
3587 border-radius: 8px;
3588 padding: 1.5rem;
3589 }
3590
3591 .simple-chart h4 {
3592 margin: 0 0 1rem 0;
3593 font-size: 1rem;
3594 font-weight: 600;
3595 color: #f0f6fc;
3596 text-align: center;
3597 }
3598
3599 .chart-bars {
3600 display: flex;
3601 flex-direction: column;
3602 gap: 0.75rem;
3603 }
3604
3605 .chart-bar {
3606 display: flex;
3607 align-items: center;
3608 gap: 1rem;
3609 }
3610
3611 .bar-label {
3612 min-width: 120px;
3613 font-size: 0.8rem;
3614 color: #8b949e;
3615 text-align: right;
3616 }
3617
3618 .bar-container {
3619 flex: 1;
3620 display: flex;
3621 align-items: center;
3622 gap: 0.5rem;
3623 position: relative;
3624 }
3625
3626 .bar-fill {
3627 height: 20px;
3628 border-radius: 4px;
3629 transition: width 0.6s ease;
3630 position: relative;
3631 }
3632
3633 .cpu-bar {
3634 background: linear-gradient(90deg, #f85149, #ff6b6b);
3635 }
3636
3637 .memory-bar {
3638 background: linear-gradient(90deg, #a5a5ff, #b39ddb);
3639 }
3640
3641 .network-bar {
3642 background: linear-gradient(90deg, #3fb950, #a5d6a7);
3643 }
3644
3645 .bar-value {
3646 font-size: 0.8rem;
3647 color: #f0f6fc;
3648 font-weight: 600;
3649 min-width: 50px;
3650 }
3651
3652 @media (max-width: 1024px) {
3653 .simple-charts {
3654 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
3655 }
3656
3657 .bar-label {
3658 min-width: 100px;
3659 font-size: 0.75rem;
3660 }
3661 }
3662
3663 @media (max-width: 768px) {
3664 .tasks-grid {
3665 grid-template-columns: 1fr;
3666 }
3667 .metrics-grid {
3668 grid-template-columns: 1fr;
3669 }
3670 }
3671 "#
3672 }
3673
3674 fn get_light_theme_styles(&self) -> &'static str {
3676 r#"
3677 body {
3678 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
3679 margin: 0;
3680 padding: 20px;
3681 background: #ffffff;
3682 color: #24292f;
3683 line-height: 1.6;
3684 }
3685
3686 .container {
3687 max-width: 1400px;
3688 margin: 0 auto;
3689 background: #f6f8fa;
3690 border: 1px solid #d0d7de;
3691 border-radius: 12px;
3692 overflow: hidden;
3693 }
3694
3695 .header {
3696 background: linear-gradient(135deg, #0969da 0%, #8250df 100%);
3697 padding: 2rem;
3698 text-align: center;
3699 color: white;
3700 }
3701
3702 .summary {
3703 display: grid;
3704 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
3705 gap: 1.5rem;
3706 padding: 2rem;
3707 background: #ffffff;
3708 }
3709
3710 .summary-card {
3711 background: #f6f8fa;
3712 border: 1px solid #d0d7de;
3713 padding: 1.5rem;
3714 border-radius: 8px;
3715 text-align: center;
3716 }
3717
3718 .task-card {
3719 background: #ffffff;
3720 border: 1px solid #d0d7de;
3721 border-radius: 10px;
3722 overflow: hidden;
3723 transition: transform 0.2s ease;
3724 position: relative;
3725 }
3726
3727 .task-card:hover {
3728 transform: translateY(-2px);
3729 box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
3730 }
3731
3732 /* Add more light theme styles as needed */
3733 "#
3734 }
3735}
3736
3737#[cfg(test)]
3738mod tests {
3739 use super::*;
3740 use crate::async_memory::resource_monitor::{
3741 BottleneckType, ComponentScores, CpuMetrics, CriticalPathAnalysis, EfficiencyExplanation,
3742 HotMetrics, IoMetrics, MemoryMetrics, NetworkMetrics, SourceLocation, TaskResourceProfile,
3743 TaskType,
3744 };
3745
3746 fn create_test_profile(
3748 task_name: &str,
3749 task_type: TaskType,
3750 cpu_usage: f64,
3751 memory_bytes: u64,
3752 efficiency: f64,
3753 ) -> TaskResourceProfile {
3754 TaskResourceProfile {
3755 task_id: 1u128,
3756 task_name: task_name.to_string(),
3757 task_type,
3758 start_time: 1000,
3759 end_time: Some(2000),
3760 duration_ms: Some(1000.0),
3761 cpu_metrics: CpuMetrics {
3762 usage_percent: cpu_usage,
3763 time_user_ms: 100.0,
3764 time_kernel_ms: 50.0,
3765 context_switches: 10,
3766 cpu_cycles: 1000000,
3767 instructions: 500000,
3768 cache_misses: 100,
3769 branch_misses: 50,
3770 core_affinity: vec![0],
3771 },
3772 memory_metrics: MemoryMetrics {
3773 allocated_bytes: memory_bytes,
3774 peak_bytes: memory_bytes + 1024,
3775 current_bytes: memory_bytes,
3776 allocation_count: 5,
3777 deallocation_count: 3,
3778 page_faults: 10,
3779 heap_fragmentation: 0.1,
3780 memory_bandwidth_mbps: 1000.0,
3781 },
3782 io_metrics: IoMetrics {
3783 bytes_read: 1024,
3784 bytes_written: 512,
3785 read_operations: 10,
3786 write_operations: 5,
3787 sync_operations: 3,
3788 async_operations: 12,
3789 avg_latency_us: 100.0,
3790 bandwidth_mbps: 10.0,
3791 queue_depth: 4,
3792 io_wait_percent: 5.0,
3793 },
3794 network_metrics: NetworkMetrics {
3795 bytes_sent: 2048,
3796 bytes_received: 1536,
3797 packets_sent: 100,
3798 packets_received: 95,
3799 connections_active: 5,
3800 connections_established: 10,
3801 connection_errors: 1,
3802 latency_avg_ms: 10.0,
3803 throughput_mbps: 5.0,
3804 retransmissions: 2,
3805 },
3806 gpu_metrics: None,
3807 efficiency_score: efficiency,
3808 resource_balance: 0.8,
3809 bottleneck_type: BottleneckType::Balanced,
3810 source_location: SourceLocation {
3811 file_path: "test.rs".to_string(),
3812 line_number: 42,
3813 function_name: "test_function".to_string(),
3814 module_path: "test::module".to_string(),
3815 crate_name: "test_crate".to_string(),
3816 },
3817 hot_metrics: HotMetrics {
3818 cpu_hotspots: vec![],
3819 memory_hotspots: vec![],
3820 io_hotspots: vec![],
3821 network_hotspots: vec![],
3822 critical_path_analysis: CriticalPathAnalysis {
3823 total_execution_time_ms: 1000.0,
3824 critical_path_time_ms: 800.0,
3825 parallelization_potential: 0.5,
3826 blocking_operations: vec![],
3827 },
3828 },
3829 efficiency_explanation: EfficiencyExplanation {
3830 overall_score: efficiency,
3831 component_scores: ComponentScores {
3832 cpu_efficiency: efficiency,
3833 memory_efficiency: efficiency,
3834 io_efficiency: efficiency,
3835 network_efficiency: efficiency,
3836 resource_balance: 0.8,
3837 },
3838 recommendations: vec![],
3839 bottleneck_analysis: "No bottlenecks detected".to_string(),
3840 optimization_potential: 0.2,
3841 },
3842 }
3843 }
3844
3845 #[test]
3846 fn test_visualization_config_default() {
3847 let config = VisualizationConfig::default();
3848
3849 assert_eq!(config.title, "Async Task Performance Analysis");
3850 assert!(matches!(config.theme, Theme::Dark));
3851 assert!(config.include_charts);
3852 assert!(config.include_baselines);
3853 assert!(config.include_rankings);
3854 assert!(config.include_efficiency_breakdown);
3855 }
3856
3857 #[test]
3858 fn test_visualization_generator_new() {
3859 let generator = VisualizationGenerator::new();
3860 assert_eq!(generator.config.title, "Async Task Performance Analysis");
3861 }
3862
3863 #[test]
3864 fn test_visualization_generator_with_config() {
3865 let custom_config = VisualizationConfig {
3866 title: "Custom Analysis".to_string(),
3867 theme: Theme::Light,
3868 include_charts: false,
3869 include_baselines: false,
3870 include_rankings: false,
3871 include_efficiency_breakdown: false,
3872 };
3873
3874 let generator = VisualizationGenerator::with_config(custom_config.clone());
3875 assert_eq!(generator.config.title, "Custom Analysis");
3876 assert!(matches!(generator.config.theme, Theme::Light));
3877 assert!(!generator.config.include_charts);
3878 }
3879
3880 #[test]
3881 fn test_calculate_baselines() {
3882 let generator = VisualizationGenerator::new();
3883 let mut profiles = HashMap::new();
3884
3885 profiles.insert(
3886 1u128,
3887 create_test_profile("task1", TaskType::CpuIntensive, 50.0, 1024 * 1024, 0.8),
3888 );
3889 profiles.insert(
3890 2u128,
3891 create_test_profile("task2", TaskType::IoIntensive, 30.0, 2048 * 1024, 0.6),
3892 );
3893
3894 let baselines = generator.calculate_baselines(&profiles);
3895
3896 assert_eq!(baselines.avg_cpu_percent, 40.0);
3897 assert_eq!(baselines.avg_memory_mb, 1.5);
3898 assert_eq!(baselines.avg_io_mbps, 10.0);
3899 assert_eq!(baselines.avg_network_mbps, 5.0);
3900 assert_eq!(baselines.avg_efficiency_score, 0.7);
3901 }
3902
3903 #[test]
3904 fn test_calculate_rankings() {
3905 let generator = VisualizationGenerator::new();
3906 let mut profiles = HashMap::new();
3907
3908 profiles.insert(
3910 1u128,
3911 create_test_profile("task1", TaskType::CpuIntensive, 50.0, 1024 * 1024, 0.9),
3912 );
3913 profiles.insert(
3914 2u128,
3915 create_test_profile("task2", TaskType::CpuIntensive, 30.0, 2048 * 1024, 0.7),
3916 );
3917 profiles.insert(
3918 3u128,
3919 create_test_profile("task3", TaskType::IoIntensive, 20.0, 512 * 1024, 0.8),
3920 );
3921
3922 let rankings = generator.calculate_rankings(&profiles);
3923
3924 let task1_ranking = rankings.get(&1u128).expect("Task 1 should have ranking");
3926 let task2_ranking = rankings.get(&2u128).expect("Task 2 should have ranking");
3927
3928 assert_eq!(task1_ranking.rank, 1); assert_eq!(task1_ranking.total_in_category, 2);
3930 assert_eq!(task1_ranking.category_name, "CpuIntensive");
3931
3932 assert_eq!(task2_ranking.rank, 2);
3933 assert_eq!(task2_ranking.total_in_category, 2);
3934
3935 let task3_ranking = rankings.get(&3u128).expect("Task 3 should have ranking");
3937 assert_eq!(task3_ranking.rank, 1);
3938 assert_eq!(task3_ranking.total_in_category, 1);
3939 assert_eq!(task3_ranking.category_name, "IoIntensive");
3940 }
3941
3942 #[test]
3943 fn test_compare_to_baseline() {
3944 let generator = VisualizationGenerator::new();
3945
3946 let comp = generator.compare_to_baseline(110.0, 100.0);
3948 assert!(matches!(comp.comparison_type, ComparisonType::AboveAverage));
3949 assert_eq!(comp.difference_percent, 10.0);
3950
3951 let comp = generator.compare_to_baseline(90.0, 100.0);
3953 assert!(matches!(comp.comparison_type, ComparisonType::BelowAverage));
3954 assert_eq!(comp.difference_percent, -10.0);
3955
3956 let comp = generator.compare_to_baseline(102.0, 100.0);
3958 assert!(matches!(comp.comparison_type, ComparisonType::NearAverage));
3959 assert_eq!(comp.difference_percent, 2.0);
3960
3961 let comp = generator.compare_to_baseline(50.0, 0.0);
3963 assert_eq!(comp.difference_percent, 0.0);
3964 }
3965
3966 #[test]
3967 fn test_analyze_profiles_empty() {
3968 let generator = VisualizationGenerator::new();
3969 let profiles = HashMap::new();
3970
3971 let result = generator.analyze_profiles(&profiles);
3972 assert!(result.is_err());
3973 assert!(matches!(
3974 result.unwrap_err(),
3975 VisualizationError::NoDataAvailable
3976 ));
3977 }
3978
3979 #[test]
3980 fn test_format_comparison() {
3981 let generator = VisualizationGenerator::new();
3982
3983 let comp_above = PerformanceComparison {
3984 value: 110.0,
3985 baseline: 100.0,
3986 difference_percent: 10.5,
3987 comparison_type: ComparisonType::AboveAverage,
3988 };
3989 assert_eq!(generator.format_comparison(&comp_above), "(+10.5% vs avg)");
3990
3991 let comp_below = PerformanceComparison {
3992 value: 85.0,
3993 baseline: 100.0,
3994 difference_percent: -15.0,
3995 comparison_type: ComparisonType::BelowAverage,
3996 };
3997 assert_eq!(generator.format_comparison(&comp_below), "(-15.0% vs avg)");
3998
3999 let comp_average = PerformanceComparison {
4000 value: 102.0,
4001 baseline: 100.0,
4002 difference_percent: 2.0,
4003 comparison_type: ComparisonType::NearAverage,
4004 };
4005 assert_eq!(generator.format_comparison(&comp_average), "(≈ avg)");
4006 }
4007
4008 #[test]
4009 fn test_get_comparison_class() {
4010 let generator = VisualizationGenerator::new();
4011
4012 let comp_above = PerformanceComparison {
4013 value: 110.0,
4014 baseline: 100.0,
4015 difference_percent: 10.0,
4016 comparison_type: ComparisonType::AboveAverage,
4017 };
4018 assert_eq!(
4019 generator.get_comparison_class(&comp_above),
4020 "comparison-above"
4021 );
4022
4023 let comp_below = PerformanceComparison {
4024 value: 90.0,
4025 baseline: 100.0,
4026 difference_percent: -10.0,
4027 comparison_type: ComparisonType::BelowAverage,
4028 };
4029 assert_eq!(
4030 generator.get_comparison_class(&comp_below),
4031 "comparison-below"
4032 );
4033
4034 let comp_average = PerformanceComparison {
4035 value: 102.0,
4036 baseline: 100.0,
4037 difference_percent: 2.0,
4038 comparison_type: ComparisonType::NearAverage,
4039 };
4040 assert_eq!(
4041 generator.get_comparison_class(&comp_average),
4042 "comparison-average"
4043 );
4044 }
4045
4046 #[test]
4047 fn test_theme_styles() {
4048 let generator = VisualizationGenerator::new();
4049
4050 let dark_styles = generator.get_dark_theme_styles();
4051 assert!(dark_styles.contains("background: #0d1117"));
4052 assert!(dark_styles.contains("color: #f0f6fc"));
4053
4054 let light_styles = generator.get_light_theme_styles();
4055 assert!(light_styles.contains("background: #ffffff"));
4056 assert!(light_styles.contains("color: #24292f"));
4057 }
4058
4059 #[test]
4060 fn test_visualization_error_display() {
4061 let err = VisualizationError::NoDataAvailable;
4062 assert_eq!(format!("{}", err), "No data available for visualization");
4063
4064 let err = VisualizationError::InvalidConfiguration("test error".to_string());
4065 assert_eq!(format!("{}", err), "Invalid configuration: test error");
4066
4067 let err = VisualizationError::TemplateError("template failed".to_string());
4068 assert_eq!(
4069 format!("{}", err),
4070 "Template generation error: template failed"
4071 );
4072 }
4073}