Skip to main content

bevy_debugger_mcp/
system_profiler_processor.rs

1/// System Profiler Processor for debug command integration
2use crate::brp_messages::{DebugCommand, DebugResponse};
3use crate::debug_command_processor::DebugCommandProcessor;
4use crate::error::{Error, Result};
5use crate::system_profiler::{SystemProfiler, ExportFormat};
6use async_trait::async_trait;
7use std::sync::Arc;
8use std::time::Duration;
9use tracing::info;
10
11/// System profiler processor for debug commands
12pub struct SystemProfilerProcessor {
13    /// System profiler instance
14    profiler: Arc<SystemProfiler>,
15}
16
17impl SystemProfilerProcessor {
18    /// Create new system profiler processor
19    pub fn new(profiler: Arc<SystemProfiler>) -> Self {
20        Self { profiler }
21    }
22}
23
24#[async_trait]
25impl DebugCommandProcessor for SystemProfilerProcessor {
26    async fn process(&self, command: DebugCommand) -> Result<DebugResponse> {
27        match command {
28            DebugCommand::ProfileSystem {
29                system_name,
30                duration_ms,
31                track_allocations,
32            } => {
33                info!("Processing ProfileSystem command for: {}", system_name);
34                
35                // Start profiling
36                self.profiler
37                    .start_profiling(system_name.clone(), duration_ms, track_allocations)
38                    .await?;
39                
40                // If duration specified, wait and collect results
41                if let Some(duration) = duration_ms {
42                    // For short durations, wait synchronously
43                    if duration <= 5000 {
44                        tokio::time::sleep(Duration::from_millis(duration)).await;
45                        let profile = self.profiler.stop_profiling(&system_name).await?;
46                        return Ok(DebugResponse::SystemProfile(profile));
47                    }
48                }
49                
50                // For long durations or no duration, return started response
51                Ok(DebugResponse::ProfilingStarted {
52                    system_name,
53                    duration_ms,
54                })
55            }
56            _ => Err(Error::DebugError("Unsupported command for SystemProfilerProcessor".to_string())),
57        }
58    }
59    
60    async fn validate(&self, command: &DebugCommand) -> Result<()> {
61        match command {
62            DebugCommand::ProfileSystem { system_name, duration_ms, .. } => {
63                // Validate system name
64                if system_name.is_empty() {
65                    return Err(Error::DebugError("System name cannot be empty".to_string()));
66                }
67                
68                if system_name.len() > 256 {
69                    return Err(Error::DebugError("System name too long (max 256 chars)".to_string()));
70                }
71                
72                // Validate duration
73                if let Some(duration) = duration_ms {
74                    if *duration == 0 {
75                        return Err(Error::DebugError("Duration must be greater than 0".to_string()));
76                    }
77                    if *duration > 300_000 {  // 5 minutes max
78                        return Err(Error::DebugError("Duration too long (max 5 minutes)".to_string()));
79                    }
80                }
81                
82                Ok(())
83            }
84            _ => Ok(()),
85        }
86    }
87    
88    fn estimate_processing_time(&self, command: &DebugCommand) -> Duration {
89        match command {
90            DebugCommand::ProfileSystem { duration_ms, .. } => {
91                // Processing time is roughly the profiling duration
92                Duration::from_millis(duration_ms.unwrap_or(5000))
93            }
94            _ => Duration::from_millis(10),
95        }
96    }
97    
98    fn supports_command(&self, command: &DebugCommand) -> bool {
99        matches!(command, DebugCommand::ProfileSystem { .. })
100    }
101}
102
103/// Extended profiler commands processor for additional functionality
104pub struct ExtendedProfilerProcessor {
105    /// System profiler instance
106    profiler: Arc<SystemProfiler>,
107}
108
109impl ExtendedProfilerProcessor {
110    /// Create new extended profiler processor
111    pub fn new(profiler: Arc<SystemProfiler>) -> Self {
112        Self { profiler }
113    }
114    
115    /// Stop profiling for a system
116    pub async fn stop_profiling(&self, system_name: &str) -> Result<DebugResponse> {
117        let profile = self.profiler.stop_profiling(system_name).await?;
118        Ok(DebugResponse::SystemProfile(profile))
119    }
120    
121    /// Get profiling history
122    pub async fn get_history(&self, system_name: &str) -> Result<DebugResponse> {
123        let samples = self.profiler.get_system_history(system_name).await;
124        
125        // Convert to response format
126        let frame_count = samples.len();
127        Ok(DebugResponse::ProfileHistory {
128            system_name: system_name.to_string(),
129            samples,
130            frame_count,
131        })
132    }
133    
134    /// Get detected anomalies
135    pub async fn get_anomalies(&self) -> Result<DebugResponse> {
136        let anomalies = self.profiler.get_anomalies().await;
137        
138        Ok(DebugResponse::PerformanceAnomalies {
139            count: anomalies.len(),
140            anomalies: anomalies.into_iter().map(|a| {
141                serde_json::json!({
142                    "frame": a.frame_number,
143                    "system": a.system_name,
144                    "execution_ms": a.execution_time_ms,
145                    "expected_ms": a.expected_time_ms,
146                    "severity": a.severity,
147                    "detected_at_us": a.detected_at_us,
148                })
149            }).collect(),
150        })
151    }
152    
153    /// Export profiling data
154    pub async fn export_data(&self, format: ExportFormat) -> Result<String> {
155        self.profiler.export_profiling_data(format).await
156    }
157    
158    /// Clear all profiling data
159    pub async fn clear_data(&self) -> Result<()> {
160        self.profiler.clear_profiling_data().await;
161        Ok(())
162    }
163    
164    /// Update system dependencies
165    pub async fn update_dependencies(
166        &self,
167        system_name: String,
168        dependencies: Vec<String>,
169    ) -> Result<()> {
170        self.profiler.update_dependency_graph(system_name, dependencies).await;
171        Ok(())
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::brp_client::BrpClient;
179    use crate::config::Config;
180    use tokio::sync::RwLock;
181
182    #[tokio::test]
183    async fn test_profiler_processor_creation() {
184        let config = Config {
185            bevy_brp_host: "localhost".to_string(),
186            bevy_brp_port: 15702,
187            mcp_port: 3000,
188        };
189        let brp_client = Arc::new(RwLock::new(BrpClient::new(&config)));
190        let profiler = Arc::new(SystemProfiler::new(brp_client));
191        let processor = SystemProfilerProcessor::new(profiler);
192        
193        let command = DebugCommand::ProfileSystem {
194            system_name: "test".to_string(),
195            duration_ms: Some(1000),
196            track_allocations: Some(false),
197        };
198        
199        assert!(processor.supports_command(&command));
200    }
201    
202    #[tokio::test]
203    async fn test_command_validation() {
204        let config = Config {
205            bevy_brp_host: "localhost".to_string(),
206            bevy_brp_port: 15702,
207            mcp_port: 3000,
208        };
209        let brp_client = Arc::new(RwLock::new(BrpClient::new(&config)));
210        let profiler = Arc::new(SystemProfiler::new(brp_client));
211        let processor = SystemProfilerProcessor::new(profiler);
212        
213        // Valid command
214        let valid_command = DebugCommand::ProfileSystem {
215            system_name: "test_system".to_string(),
216            duration_ms: Some(1000),
217            track_allocations: Some(false),
218        };
219        assert!(processor.validate(&valid_command).await.is_ok());
220        
221        // Invalid: empty name
222        let invalid_command = DebugCommand::ProfileSystem {
223            system_name: "".to_string(),
224            duration_ms: Some(1000),
225            track_allocations: Some(false),
226        };
227        assert!(processor.validate(&invalid_command).await.is_err());
228        
229        // Invalid: duration too long
230        let invalid_command = DebugCommand::ProfileSystem {
231            system_name: "test".to_string(),
232            duration_ms: Some(400_000),
233            track_allocations: Some(false),
234        };
235        assert!(processor.validate(&invalid_command).await.is_err());
236    }
237    
238    #[test]
239    fn test_processing_time_estimation() {
240        let config = Config {
241            bevy_brp_host: "localhost".to_string(),
242            bevy_brp_port: 15702,
243            mcp_port: 3000,
244        };
245        let brp_client = Arc::new(RwLock::new(BrpClient::new(&config)));
246        let profiler = Arc::new(SystemProfiler::new(brp_client));
247        let processor = SystemProfilerProcessor::new(profiler);
248        
249        let command = DebugCommand::ProfileSystem {
250            system_name: "test".to_string(),
251            duration_ms: Some(3000),
252            track_allocations: Some(false),
253        };
254        
255        let estimated_time = processor.estimate_processing_time(&command);
256        assert_eq!(estimated_time.as_millis(), 3000);
257    }
258}