async_inspect/profile/
reporter.rs

1//! Performance report generation
2
3use super::Profiler;
4use std::fmt::Write as FmtWrite;
5
6/// Performance report generator
7pub struct PerformanceReporter<'a> {
8    profiler: &'a Profiler,
9}
10
11impl<'a> PerformanceReporter<'a> {
12    /// Create a new performance reporter
13    #[must_use]
14    pub fn new(profiler: &'a Profiler) -> Self {
15        Self { profiler }
16    }
17
18    /// Print a comprehensive performance report
19    pub fn print_report(&self) {
20        self.print_header();
21        self.print_overall_stats();
22        self.print_bottlenecks();
23        self.print_hot_paths();
24        self.print_slowest_tasks();
25        self.print_await_stats();
26        self.print_efficiency_analysis();
27    }
28
29    /// Print report header
30    fn print_header(&self) {
31        println!("\n╔════════════════════════════════════════════════════════════╗");
32        println!("║           async-inspect - Performance Report              ║");
33        println!("╚════════════════════════════════════════════════════════════╝\n");
34    }
35
36    /// Print overall statistics
37    fn print_overall_stats(&self) {
38        println!("┌────────────────────────────────────────────────────────────┐");
39        println!("│ Overall Statistics                                         │");
40        println!("└────────────────────────────────────────────────────────────┘");
41
42        let stats = self.profiler.calculate_stats();
43        let all_metrics = self.profiler.all_metrics();
44
45        println!("  Total Tasks:     {}", all_metrics.len());
46        println!(
47            "  Completed:       {}",
48            all_metrics.iter().filter(|m| m.completed).count()
49        );
50        println!();
51        println!("  Duration Stats:");
52        println!(
53            "    Min:           {:.2}ms",
54            stats.min.as_secs_f64() * 1000.0
55        );
56        println!(
57            "    Max:           {:.2}ms",
58            stats.max.as_secs_f64() * 1000.0
59        );
60        println!(
61            "    Mean:          {:.2}ms",
62            stats.mean.as_secs_f64() * 1000.0
63        );
64        println!(
65            "    Median (p50):  {:.2}ms",
66            stats.median.as_secs_f64() * 1000.0
67        );
68        println!(
69            "    p95:           {:.2}ms",
70            stats.p95.as_secs_f64() * 1000.0
71        );
72        println!(
73            "    p99:           {:.2}ms",
74            stats.p99.as_secs_f64() * 1000.0
75        );
76        println!("    Std Dev:       {:.2}ms", stats.std_dev * 1000.0);
77        println!();
78    }
79
80    /// Print bottleneck analysis
81    fn print_bottlenecks(&self) {
82        let bottlenecks = self.profiler.identify_bottlenecks();
83
84        println!("┌────────────────────────────────────────────────────────────┐");
85        println!("│ Bottleneck Analysis                                        │");
86        println!("└────────────────────────────────────────────────────────────┘");
87
88        if bottlenecks.is_empty() {
89            println!("  ✅ No bottlenecks detected\n");
90            return;
91        }
92
93        println!(
94            "  ⚠️  Found {} potential bottleneck(s):\n",
95            bottlenecks.len()
96        );
97
98        for (i, metrics) in bottlenecks.iter().enumerate().take(10) {
99            println!(
100                "  {}. {} (#{}) - {:.2}ms",
101                i + 1,
102                metrics.name,
103                metrics.task_id.as_u64(),
104                metrics.total_duration.as_secs_f64() * 1000.0
105            );
106            println!(
107                "     Running: {:.2}ms | Blocked: {:.2}ms | Efficiency: {:.1}%",
108                metrics.running_time.as_secs_f64() * 1000.0,
109                metrics.blocked_time.as_secs_f64() * 1000.0,
110                metrics.efficiency() * 100.0
111            );
112        }
113        println!();
114    }
115
116    /// Print hot path analysis
117    fn print_hot_paths(&self) {
118        let hot_paths = self.profiler.get_hot_paths();
119
120        println!("┌────────────────────────────────────────────────────────────┐");
121        println!("│ Hot Paths (Most Frequently Executed)                      │");
122        println!("└────────────────────────────────────────────────────────────┘");
123
124        if hot_paths.is_empty() {
125            println!("  No hot paths identified\n");
126            return;
127        }
128
129        println!("  Top execution paths:\n");
130
131        for (i, path) in hot_paths.iter().enumerate().take(10) {
132            println!("  {}. {} ", i + 1, path.path);
133            println!(
134                "     Executions: {} | Total: {:.2}ms | Avg: {:.2}ms",
135                path.execution_count,
136                path.total_time.as_secs_f64() * 1000.0,
137                path.avg_time.as_secs_f64() * 1000.0
138            );
139        }
140        println!();
141    }
142
143    /// Print slowest tasks
144    fn print_slowest_tasks(&self) {
145        let slowest = self.profiler.slowest_tasks(10);
146
147        println!("┌────────────────────────────────────────────────────────────┐");
148        println!("│ Slowest Tasks                                              │");
149        println!("└────────────────────────────────────────────────────────────┘");
150
151        if slowest.is_empty() {
152            println!("  No tasks to analyze\n");
153            return;
154        }
155
156        for (i, metrics) in slowest.iter().enumerate() {
157            println!(
158                "  {}. {} (#{}) - {:.2}ms",
159                i + 1,
160                metrics.name,
161                metrics.task_id.as_u64(),
162                metrics.total_duration.as_secs_f64() * 1000.0
163            );
164            println!(
165                "     Polls: {} | Awaits: {} | Avg poll: {:.2}ms",
166                metrics.poll_count,
167                metrics.await_count,
168                metrics.avg_poll_duration.as_secs_f64() * 1000.0
169            );
170        }
171        println!();
172    }
173
174    /// Print await point statistics
175    fn print_await_stats(&self) {
176        let stats = self.profiler.await_stats();
177
178        println!("┌────────────────────────────────────────────────────────────┐");
179        println!("│ Await Point Analysis                                       │");
180        println!("└────────────────────────────────────────────────────────────┘");
181
182        if stats.count == 0 {
183            println!("  No await points recorded\n");
184            return;
185        }
186
187        println!("  Total Await Points: {}", stats.count);
188        println!();
189        println!("  Await Duration Stats:");
190        println!(
191            "    Min:           {:.2}ms",
192            stats.min.as_secs_f64() * 1000.0
193        );
194        println!(
195            "    Max:           {:.2}ms",
196            stats.max.as_secs_f64() * 1000.0
197        );
198        println!(
199            "    Mean:          {:.2}ms",
200            stats.mean.as_secs_f64() * 1000.0
201        );
202        println!(
203            "    Median (p50):  {:.2}ms",
204            stats.median.as_secs_f64() * 1000.0
205        );
206        println!(
207            "    p95:           {:.2}ms",
208            stats.p95.as_secs_f64() * 1000.0
209        );
210        println!(
211            "    p99:           {:.2}ms",
212            stats.p99.as_secs_f64() * 1000.0
213        );
214        println!();
215    }
216
217    /// Print efficiency analysis
218    fn print_efficiency_analysis(&self) {
219        let least_efficient = self.profiler.least_efficient_tasks(5);
220
221        println!("┌────────────────────────────────────────────────────────────┐");
222        println!("│ Efficiency Analysis (Least Efficient Tasks)               │");
223        println!("└────────────────────────────────────────────────────────────┘");
224
225        if least_efficient.is_empty() {
226            println!("  No tasks to analyze\n");
227            return;
228        }
229
230        println!("  Tasks with highest blocked time ratio:\n");
231
232        for (i, metrics) in least_efficient.iter().enumerate() {
233            let efficiency_pct = metrics.efficiency() * 100.0;
234            let blocked_pct =
235                (metrics.blocked_time.as_secs_f64() / metrics.total_duration.as_secs_f64()) * 100.0;
236
237            println!(
238                "  {}. {} (#{}) - {:.1}% efficient",
239                i + 1,
240                metrics.name,
241                metrics.task_id.as_u64(),
242                efficiency_pct
243            );
244            println!(
245                "     Total: {:.2}ms | Running: {:.2}ms ({:.1}%) | Blocked: {:.2}ms ({:.1}%)",
246                metrics.total_duration.as_secs_f64() * 1000.0,
247                metrics.running_time.as_secs_f64() * 1000.0,
248                efficiency_pct,
249                metrics.blocked_time.as_secs_f64() * 1000.0,
250                blocked_pct
251            );
252        }
253        println!();
254    }
255
256    /// Generate a compact performance summary
257    #[must_use]
258    pub fn generate_summary(&self) -> String {
259        let mut summary = String::new();
260        let stats = self.profiler.calculate_stats();
261        let bottlenecks = self.profiler.identify_bottlenecks();
262
263        writeln!(summary, "Performance Summary:").unwrap();
264        writeln!(summary, "  Tasks: {}", self.profiler.all_metrics().len()).unwrap();
265        writeln!(
266            summary,
267            "  Mean duration: {:.2}ms",
268            stats.mean.as_secs_f64() * 1000.0
269        )
270        .unwrap();
271        writeln!(
272            summary,
273            "  p95 duration: {:.2}ms",
274            stats.p95.as_secs_f64() * 1000.0
275        )
276        .unwrap();
277        writeln!(summary, "  Bottlenecks: {}", bottlenecks.len()).unwrap();
278
279        summary
280    }
281
282    /// Print recommendations based on profiling data
283    pub fn print_recommendations(&self) {
284        println!("┌────────────────────────────────────────────────────────────┐");
285        println!("│ Optimization Recommendations                               │");
286        println!("└────────────────────────────────────────────────────────────┘");
287
288        let bottlenecks = self.profiler.identify_bottlenecks();
289        let least_efficient = self.profiler.least_efficient_tasks(3);
290        let busiest = self.profiler.busiest_tasks(3);
291
292        let mut recommendations = Vec::new();
293
294        if !bottlenecks.is_empty() {
295            recommendations.push(format!(
296                "⚠️  {} bottleneck(s) detected - consider optimizing slow tasks",
297                bottlenecks.len()
298            ));
299        }
300
301        if !least_efficient.is_empty() {
302            let avg_efficiency: f64 = least_efficient.iter().map(|m| m.efficiency()).sum::<f64>()
303                / least_efficient.len() as f64;
304
305            if avg_efficiency < 0.5 {
306                recommendations.push(
307                    "⚡ Low efficiency detected - tasks spending too much time blocked".to_string(),
308                );
309                recommendations.push(
310                    "   → Consider reducing await dependencies or using timeouts".to_string(),
311                );
312            }
313        }
314
315        if !busiest.is_empty() {
316            let max_polls = busiest[0].poll_count;
317            if max_polls > 100 {
318                recommendations.push(format!(
319                    "🔄 Task with {max_polls} polls detected - possible busy loop or fine-grained awaits"
320                ));
321                recommendations.push(
322                    "   → Consider batching operations or using coarser-grained awaits".to_string(),
323                );
324            }
325        }
326
327        let hot_paths = self.profiler.get_hot_paths();
328        if let Some(hottest) = hot_paths.first() {
329            if hottest.execution_count > 100 {
330                recommendations.push(format!(
331                    "🔥 Hot path detected: '{}' executed {} times",
332                    hottest.path, hottest.execution_count
333                ));
334                recommendations
335                    .push("   → Consider caching or memoization if appropriate".to_string());
336            }
337        }
338
339        if recommendations.is_empty() {
340            println!("  ✅ No major performance issues detected!");
341            println!("  ✨ Your async code looks well-optimized.");
342        } else {
343            for rec in recommendations {
344                println!("  {rec}");
345            }
346        }
347
348        println!();
349    }
350}