use super::profiler_types::*;
use anyhow::{Context, Result};
use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use tracing::{debug, info, warn};
use crate::device_info::{MobileDeviceDetector, MobileDeviceInfo, ThermalState};
use crate::mobile_performance_profiler::collector::{CollectionStatistics, MobileMetricsCollector};
use crate::mobile_performance_profiler::config::MobileProfilerConfig;
use crate::mobile_performance_profiler::types::{
AlertManagerConfig, AnalysisConfig, BottleneckDetectionConfig, ExportManagerConfig,
HealthStatus, OptimizationEngineConfig, OptimizationSuggestion, PerformanceAlert,
PerformanceBottleneck, PlatformCapabilities, ProfilingData, ProfilingEvent,
RealTimeMonitoringConfig, SessionInfo, SessionMetadata, SystemHealth, TrendingMetrics,
};
impl ProfilingSession {
pub(crate) fn new(device_info: MobileDeviceInfo) -> Result<Self> {
Ok(Self {
session_id: None,
start_time: None,
end_time: None,
device_info: Some(device_info),
metadata: SessionMetadata::default(),
events: VecDeque::new(),
config_snapshot: None,
state: SessionState::Idle,
max_events: 10000, })
}
pub(crate) fn start_session(&mut self) -> Result<String> {
if self.state != SessionState::Idle {
return Err(anyhow::anyhow!("Session is not in idle state"));
}
self.state = SessionState::Starting;
let session_id = format!("session_{}", chrono::Utc::now().timestamp_millis());
self.session_id = Some(session_id.clone());
self.start_time = Some(Instant::now());
self.end_time = None;
self.events.clear();
self.metadata.session_id = session_id.clone();
self.metadata.start_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() as u64;
self.state = SessionState::Active;
Ok(session_id)
}
pub(crate) fn end_session(&mut self) -> Result<()> {
if self.state != SessionState::Active && self.state != SessionState::Paused {
return Err(anyhow::anyhow!("No active session to end"));
}
self.state = SessionState::Stopping;
self.end_time = Some(Instant::now());
self.metadata.end_time =
Some(SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() as u64);
self.state = SessionState::Completed;
Ok(())
}
pub(crate) fn add_event(&mut self, event: ProfilingEvent) {
self.events.push_back(event);
while self.events.len() > self.max_events {
self.events.pop_front();
}
}
pub(crate) fn get_session_info(&self) -> Result<SessionInfo> {
Ok(SessionInfo {
id: self.session_id.clone().unwrap_or_default(),
start_time: self.metadata.start_time,
end_time: self.metadata.end_time,
duration_ms: self.calculate_duration_ms(),
device_info: self.device_info.clone().unwrap_or_default(),
metadata: self.metadata.clone(),
})
}
pub(crate) fn get_all_events(&self) -> Vec<ProfilingEvent> {
self.events.iter().cloned().collect()
}
pub(crate) fn calculate_duration_ms(&self) -> Option<u64> {
if let (Some(start), Some(end)) = (self.start_time, self.end_time) {
Some(end.duration_since(start).as_millis() as u64)
} else {
self.start_time.map(|start| start.elapsed().as_millis() as u64)
}
}
}
impl BottleneckDetector {
pub(crate) fn new(_config: MobileProfilerConfig) -> Result<Self> {
Ok(Self {
config: BottleneckDetectionConfig::default(),
active_bottlenecks: HashMap::new(),
bottleneck_history: VecDeque::new(),
detection_rules: Vec::new(),
severity_calculator: SeverityCalculator {
rules: Vec::new(),
weights: HashMap::new(),
},
historical_analyzer: HistoricalAnalyzer {
history_window: Duration::from_secs(300),
trend_detectors: Vec::new(),
statistical_models: Vec::new(),
},
detection_stats: BottleneckDetectionStats::default(),
})
}
pub(crate) fn get_active_bottlenecks(&self) -> Vec<PerformanceBottleneck> {
self.active_bottlenecks.values().cloned().collect()
}
pub(crate) fn get_all_bottlenecks(&self) -> Vec<PerformanceBottleneck> {
self.active_bottlenecks.values().cloned().collect()
}
pub(crate) fn update_config(&mut self, config: MobileProfilerConfig) -> Result<()> {
self.config.enabled = config.enabled;
self.config.detection_interval_ms = config.sampling.interval_ms;
debug!("Updated bottleneck detector configuration");
Ok(())
}
}
impl OptimizationEngine {
pub(crate) fn new(_config: MobileProfilerConfig) -> Result<Self> {
Ok(Self {
config: OptimizationEngineConfig::default(),
active_suggestions: HashMap::new(),
optimization_rules: Vec::new(),
suggestion_ranker: SuggestionRanker {
ranking_algorithms: Vec::new(),
preference_weights: HashMap::new(),
},
impact_estimator: ImpactEstimator {
impact_models: Vec::new(),
historical_impacts: HashMap::new(),
},
suggestion_history: VecDeque::new(),
engine_stats: OptimizationEngineStats::default(),
})
}
pub(crate) fn get_active_suggestions(&self) -> Vec<OptimizationSuggestion> {
self.active_suggestions.values().cloned().collect()
}
pub(crate) fn get_all_suggestions(&self) -> Vec<OptimizationSuggestion> {
self.active_suggestions.values().cloned().collect()
}
pub(crate) fn update_config(&mut self, config: MobileProfilerConfig) -> Result<()> {
self.config.enabled = config.enabled;
self.config.generation_interval_ms = config.sampling.interval_ms;
debug!("Updated optimization engine configuration");
Ok(())
}
}
impl RealTimeMonitor {
pub(crate) fn new(_config: MobileProfilerConfig) -> Result<Self> {
Ok(Self {
config: RealTimeMonitoringConfig::default(),
current_state: RealTimeState {
performance_score: 85.0,
active_alerts: Vec::new(),
trending_metrics: TrendingMetrics::default(),
system_health: SystemHealth::default(),
last_update: None,
uptime: Duration::ZERO,
},
alert_manager: AlertManager::new(_config.clone())?,
live_metrics: Arc::new(RwLock::new(VecDeque::new())),
trending_metrics: TrendingMetrics::default(),
system_health: SystemHealth::default(),
monitor_stats: MonitoringStats::default(),
_monitor_thread: None,
})
}
pub(crate) fn start_monitoring(&mut self) -> Result<()> {
self.current_state.last_update = Some(Instant::now());
Ok(())
}
pub(crate) fn stop_monitoring(&mut self) -> Result<()> {
self.current_state.performance_score = 0.0;
self.current_state.active_alerts.clear();
self.current_state.last_update = None;
if let Ok(mut metrics) = self.live_metrics.write() {
metrics.clear();
}
if let Some(handle) = self._monitor_thread.take() {
drop(handle);
}
info!("Real-time monitoring stopped");
Ok(())
}
pub(crate) fn pause_monitoring(&mut self) -> Result<()> {
self.current_state.last_update = None;
self.monitor_stats.total_monitor_time =
self.monitor_stats.total_monitor_time.saturating_sub(Duration::from_millis(100));
info!("Real-time monitoring paused");
Ok(())
}
pub(crate) fn resume_monitoring(&mut self) -> Result<()> {
self.current_state.last_update = Some(Instant::now());
self.monitor_stats.total_monitor_time += Duration::from_millis(100);
info!("Real-time monitoring resumed");
Ok(())
}
pub(crate) fn update_config(&mut self, config: MobileProfilerConfig) -> Result<()> {
self.config.enabled = config.real_time_monitoring.enabled;
self.config.update_frequency_ms = config.real_time_monitoring.update_interval_ms;
self.config.alert_interval_ms = config.real_time_monitoring.update_interval_ms;
self.config.max_alerts = config.real_time_monitoring.max_history_points.min(100);
if !self.config.enabled {
self.stop_monitoring()?;
}
debug!("Updated real-time monitor configuration");
Ok(())
}
}
impl ProfilerExportManager {
pub(crate) fn new(_config: MobileProfilerConfig) -> Result<Self> {
Ok(Self {
config: ExportManagerConfig::default(),
formatters: HashMap::new(),
export_history: VecDeque::new(),
pending_exports: VecDeque::new(),
visualization_engine: VisualizationEngine {
chart_generator: ChartGenerator {
templates: HashMap::new(),
renderer: ChartRenderer,
},
dashboard_builder: DashboardBuilder {
templates: HashMap::new(),
widgets: HashMap::new(),
},
report_generator: ReportGenerator {
templates: HashMap::new(),
generators: HashMap::new(),
},
template_engine: TemplateEngine {
template_cache: HashMap::new(),
compiler: TemplateCompiler,
},
},
export_stats: ExportManagerStats::default(),
})
}
pub(crate) fn export_data(&self, data: &ProfilingData) -> Result<String> {
let timestamp = chrono::Utc::now().timestamp();
let export_path = format!("/tmp/claude/profiling_export_{}.json", timestamp);
let json_data =
serde_json::to_string_pretty(data).context("Failed to serialize profiling data")?;
std::fs::create_dir_all("/tmp/claude").context("Failed to create export directory")?;
std::fs::write(&export_path, json_data).context("Failed to write export file")?;
info!("Profiling data exported to: {}", export_path);
Ok(export_path)
}
pub(crate) fn generate_report(&self, data: &ProfilingData) -> Result<String> {
let session_duration = if let Some(end) = data.session_info.end_time {
Duration::from_secs(end.saturating_sub(data.session_info.start_time))
} else {
Duration::ZERO
};
let bottleneck_count = data.bottlenecks.len();
let suggestion_count = data.suggestions.len();
let metrics_count = data.metrics.len();
let events_count = data.events.len();
let avg_cpu_usage = if !data.metrics.is_empty() {
data.metrics.iter().map(|m| m.cpu.usage_percent).sum::<f32>()
/ data.metrics.len() as f32
} else {
0.0
};
let avg_memory_usage = if !data.metrics.is_empty() {
data.metrics.iter().map(|m| m.memory.heap_used_mb).sum::<f32>()
/ data.metrics.len() as f32
} else {
0.0
};
let report_html = format!(
r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TrustformRS Mobile Performance Report</title>
<style>
body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }}
.container {{ max-width: 1200px; margin: 0 auto; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
.header {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 8px 8px 0 0; }}
.header h1 {{ margin: 0; font-size: 2.5em; }}
.header .subtitle {{ margin: 10px 0 0 0; opacity: 0.9; font-size: 1.1em; }}
.section {{ padding: 30px; border-bottom: 1px solid #eee; }}
.section:last-child {{ border-bottom: none; }}
.section h2 {{ color: #333; margin: 0 0 20px 0; font-size: 1.5em; }}
.metrics-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; }}
.metric-card {{ background: #f8f9fa; padding: 20px; border-radius: 6px; border-left: 4px solid #667eea; }}
.metric-value {{ font-size: 2em; font-weight: bold; color: #667eea; margin: 0; }}
.metric-label {{ color: #666; margin: 5px 0 0 0; text-transform: uppercase; font-size: 0.8em; letter-spacing: 1px; }}
.bottlenecks, .suggestions {{ margin: 20px 0; }}
.bottleneck-item, .suggestion-item {{ background: #fff3cd; padding: 15px; margin: 10px 0; border-radius: 4px; border-left: 4px solid #ffc107; }}
.footer {{ text-align: center; padding: 20px; color: #666; font-size: 0.9em; }}
.timestamp {{ color: #888; font-size: 0.9em; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Performance Report</h1>
<div class="subtitle">TrustformRS Mobile Profiler Analysis</div>
<div class="timestamp">Generated: {}</div>
</div>
<div class="section">
<h2>Session Overview</h2>
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-value">{:.1}s</div>
<div class="metric-label">Session Duration</div>
</div>
<div class="metric-card">
<div class="metric-value">{}</div>
<div class="metric-label">Events Recorded</div>
</div>
<div class="metric-card">
<div class="metric-value">{}</div>
<div class="metric-label">Metrics Snapshots</div>
</div>
<div class="metric-card">
<div class="metric-value">{:.1}</div>
<div class="metric-label">Overall Health</div>
</div>
</div>
</div>
<div class="section">
<h2>Performance Metrics</h2>
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-value">{:.1}%</div>
<div class="metric-label">Average CPU Usage</div>
</div>
<div class="metric-card">
<div class="metric-value">{:.1} MB</div>
<div class="metric-label">Average Memory Usage</div>
</div>
<div class="metric-card">
<div class="metric-value">{}</div>
<div class="metric-label">Bottlenecks Detected</div>
</div>
<div class="metric-card">
<div class="metric-value">{}</div>
<div class="metric-label">Optimization Suggestions</div>
</div>
</div>
</div>
<div class="section">
<h2>Performance Issues</h2>
<div class="bottlenecks">
{}
</div>
</div>
<div class="section">
<h2>Optimization Recommendations</h2>
<div class="suggestions">
{}
</div>
</div>
<div class="footer">
<p>Generated by TrustformRS Mobile Performance Profiler v{}</p>
</div>
</div>
</body>
</html>
"#,
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"),
session_duration.as_secs_f64(),
events_count,
metrics_count,
data.system_health.overall_score,
avg_cpu_usage,
avg_memory_usage,
bottleneck_count,
suggestion_count,
if data.bottlenecks.is_empty() {
"<div class=\"bottleneck-item\">No performance bottlenecks detected.</div>"
.to_string()
} else {
data.bottlenecks.iter().take(5).map(|b|
format!("<div class=\"bottleneck-item\"><strong>{}</strong>: {} (Severity: {:?})</div>",
b.affected_component, b.description, b.severity)
).collect::<Vec<_>>().join("\n")
},
if data.suggestions.is_empty() {
"<div class=\"suggestion-item\">No optimization suggestions available.</div>"
.to_string()
} else {
data.suggestions.iter().take(5).map(|s|
format!("<div class=\"suggestion-item\"><strong>{}</strong>: {} (Priority: {:?})</div>",
format!("{:?}", s.suggestion_type), s.description, s.priority)
).collect::<Vec<_>>().join("\n")
},
data.profiler_version
);
Ok(report_html)
}
}
impl AlertManager {
pub(crate) fn new(_config: MobileProfilerConfig) -> Result<Self> {
Ok(Self {
config: AlertManagerConfig::default(),
active_alerts: HashMap::new(),
alert_history: VecDeque::new(),
alert_rules: Vec::new(),
notification_handlers: Vec::new(),
})
}
pub(crate) fn get_active_alerts(&self) -> Vec<PerformanceAlert> {
self.active_alerts.values().cloned().collect()
}
}
impl PerformanceAnalyzer {
pub(crate) fn new(_config: MobileProfilerConfig) -> Result<Self> {
Ok(Self {
config: AnalysisConfig::default(),
analysis_cache: HashMap::new(),
trend_data: VecDeque::new(),
performance_models: Vec::new(),
})
}
pub(crate) fn get_current_health(&self) -> Result<SystemHealth> {
let mut component_scores = HashMap::new();
let mut total_score = 0.0f32;
let mut component_count = 0usize;
let cpu_score = if self.performance_models.is_empty() {
85.0 } else {
let recent_cpu_trend = self.trend_data.iter().rev().take(10).count() as f32;
(100.0 - recent_cpu_trend * 2.0).clamp(0.0, 100.0)
};
component_scores.insert("cpu".to_string(), cpu_score);
total_score += cpu_score;
component_count += 1;
let memory_score = if self.analysis_cache.is_empty() {
80.0 } else {
let cache_utilization = (self.analysis_cache.len() as f32 / 1000.0 * 100.0).min(100.0);
(100.0 - cache_utilization).clamp(0.0, 100.0)
};
component_scores.insert("memory".to_string(), memory_score);
total_score += memory_score;
component_count += 1;
let trend_score = if self.trend_data.is_empty() {
90.0 } else {
let stability_factor = (self.trend_data.len() as f32 / 100.0).min(1.0);
70.0 + (stability_factor * 30.0) };
component_scores.insert("performance_trend".to_string(), trend_score);
total_score += trend_score;
component_count += 1;
let analysis_score = if self.performance_models.is_empty() {
75.0 } else {
let model_factor = (self.performance_models.len() as f32 / 10.0).min(1.0);
60.0 + (model_factor * 40.0) };
component_scores.insert("analysis_engine".to_string(), analysis_score);
total_score += analysis_score;
component_count += 1;
let overall_score = if component_count > 0 {
total_score / component_count as f32
} else {
50.0 };
let status = match overall_score {
90.0..=100.0 => HealthStatus::Excellent,
75.0..90.0 => HealthStatus::Good,
60.0..75.0 => HealthStatus::Healthy,
45.0..60.0 => HealthStatus::Fair,
30.0..45.0 => HealthStatus::Poor,
_ => HealthStatus::Critical,
};
let mut recommendations = Vec::new();
if cpu_score < 70.0 {
recommendations
.push("Consider reducing CPU-intensive operations during inference".to_string());
}
if memory_score < 70.0 {
recommendations
.push("Monitor memory usage and consider clearing analysis cache".to_string());
}
if trend_score < 70.0 {
recommendations.push(
"Insufficient performance trend data - allow more profiling time".to_string(),
);
}
if analysis_score < 70.0 {
recommendations.push("Consider enabling more performance analysis models".to_string());
}
if overall_score < 60.0 {
recommendations.push(
"System health is below optimal - review all performance metrics".to_string(),
);
}
if recommendations.is_empty() {
recommendations
.push("System health is good - continue current performance patterns".to_string());
}
Ok(SystemHealth {
overall_score,
component_scores,
status,
recommendations,
})
}
}
impl Default for ProfilingState {
fn default() -> Self {
Self {
is_active: false,
current_session_id: None,
start_time: None,
total_duration: Duration::ZERO,
events_recorded: 0,
snapshots_taken: 0,
last_error: None,
}
}
}
pub(crate) fn get_platform_capabilities() -> PlatformCapabilities {
let mut capabilities = PlatformCapabilities::default();
#[cfg(target_os = "ios")]
{
capabilities.ios_features = vec![
"Metal".to_string(),
"CoreML".to_string(),
"Instruments".to_string(),
"iOS Memory Pressure".to_string(),
];
}
#[cfg(target_os = "android")]
{
capabilities.android_features = vec![
"NNAPI".to_string(),
"GPU Delegate".to_string(),
"Android Profiler".to_string(),
"System Trace".to_string(),
];
}
capabilities.generic_features = vec![
"CPU Profiling".to_string(),
"Memory Profiling".to_string(),
"Network Monitoring".to_string(),
"Battery Monitoring".to_string(),
"Thermal Monitoring".to_string(),
];
capabilities
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mobile_performance_profiler::ExportFormat;
use std::time::Duration;
pub(crate) fn fast_test_config() -> MobileProfilerConfig {
let mut config = MobileProfilerConfig::default();
config.memory_profiling.enabled = false;
config.cpu_profiling.enabled = false;
config.gpu_profiling.enabled = false;
config.network_profiling.enabled = false;
config.real_time_monitoring.enabled = false;
config.sampling.interval_ms = 10000; config.sampling.max_samples = 10;
config
}
#[test]
pub(crate) fn test_profiler_creation() {
let config = fast_test_config();
let result = MobilePerformanceProfiler::new(config);
assert!(
result.is_ok(),
"Failed to create profiler: {:?}",
result.err()
);
}
#[test]
pub(crate) fn test_profiling_lifecycle() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
assert!(!profiler.is_profiling_active());
let session_id = profiler.start_profiling()?;
assert!(!session_id.is_empty());
assert!(profiler.is_profiling_active());
assert!(profiler.start_profiling().is_err());
let profiling_data = profiler.stop_profiling()?;
assert!(!profiler.is_profiling_active());
assert_eq!(profiling_data.session_info.metadata.session_id, session_id);
Ok(())
}
#[test]
pub(crate) fn test_event_recording() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
profiler.record_inference_event("model_load", Some(250.0))?;
profiler.record_inference_event("inference_start", None)?;
profiler.record_inference_event("inference_end", Some(85.0))?;
let profiling_data = profiler.stop_profiling()?;
assert_eq!(profiling_data.events.len(), 3);
Ok(())
}
#[test]
pub(crate) fn test_metrics_collection() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
std::thread::sleep(Duration::from_millis(1));
let metrics = profiler.get_current_metrics()?;
assert!(metrics.timestamp > 0);
let _stats = profiler.get_collection_stats()?;
profiler.stop_profiling()?;
Ok(())
}
#[test]
#[ignore] pub(crate) fn test_bottleneck_detection() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
std::thread::sleep(std::time::Duration::from_millis(1));
let _bottlenecks = profiler.detect_bottlenecks()?;
profiler.stop_profiling()?;
std::thread::sleep(std::time::Duration::from_millis(1));
Ok(())
}
#[test]
pub(crate) fn test_optimization_suggestions() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
let _suggestions = profiler.get_optimization_suggestions()?;
profiler.stop_profiling()?;
Ok(())
}
#[test]
pub(crate) fn test_pause_resume_profiling() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
assert!(profiler.is_profiling_active());
profiler.pause_profiling()?;
assert!(profiler.is_profiling_active());
profiler.resume_profiling()?;
assert!(profiler.is_profiling_active());
profiler.stop_profiling()?;
assert!(!profiler.is_profiling_active());
Ok(())
}
#[test]
pub(crate) fn test_config_validation() {
let mut config = MobileProfilerConfig::default();
assert!(MobilePerformanceProfiler::validate_config(&config).is_ok());
config.sampling.interval_ms = 0;
assert!(MobilePerformanceProfiler::validate_config(&config).is_err());
config = MobileProfilerConfig::default();
config.sampling.max_samples = 0;
assert!(MobilePerformanceProfiler::validate_config(&config).is_err());
config = MobileProfilerConfig::default();
config.export_config.compression_level = 10;
assert!(MobilePerformanceProfiler::validate_config(&config).is_err());
}
#[test]
pub(crate) fn test_config_update() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
let mut new_config = MobileProfilerConfig::default();
new_config.sampling.interval_ms = 200;
new_config.memory_profiling.heap_analysis = true;
profiler.update_config(new_config)?;
Ok(())
}
#[test]
pub(crate) fn test_export_functionality() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
profiler.record_inference_event("test_event", Some(100.0))?;
profiler.stop_profiling()?;
let export_path = profiler.export_data(ExportFormat::JSON)?;
assert!(!export_path.is_empty());
Ok(())
}
#[test]
pub(crate) fn test_system_health_assessment() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
let health = profiler.get_system_health()?;
assert!(health.overall_score >= 0.0 && health.overall_score <= 100.0);
profiler.stop_profiling()?;
Ok(())
}
#[test]
pub(crate) fn test_performance_report_generation() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
profiler.start_profiling()?;
profiler.record_inference_event("test_inference", Some(50.0))?;
profiler.stop_profiling()?;
let report = profiler.generate_performance_report()?;
assert!(!report.is_empty());
assert!(report.contains("html"));
Ok(())
}
#[test]
pub(crate) fn test_session_state_tracking() -> Result<()> {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config)?;
let state = profiler.get_profiling_state();
assert!(!state.is_active);
assert_eq!(state.events_recorded, 0);
profiler.start_profiling()?;
let state = profiler.get_profiling_state();
assert!(state.is_active);
assert!(state.current_session_id.is_some());
assert!(state.start_time.is_some());
profiler.record_inference_event("event1", None)?;
profiler.record_inference_event("event2", None)?;
let state = profiler.get_profiling_state();
assert_eq!(state.events_recorded, 2);
profiler.stop_profiling()?;
Ok(())
}
#[test]
pub(crate) fn test_error_handling() {
let config = fast_test_config();
let profiler = MobilePerformanceProfiler::new(config).expect("Failed to create profiler");
assert!(profiler.stop_profiling().is_err());
assert!(profiler.pause_profiling().is_err());
assert!(profiler.resume_profiling().is_err());
let mut invalid_config = MobileProfilerConfig::default();
invalid_config.sampling.interval_ms = 0;
assert!(profiler.update_config(invalid_config).is_err());
}
#[test]
pub(crate) fn test_thread_safety() -> Result<()> {
use std::sync::Arc;
use std::thread;
let config = fast_test_config();
let profiler = Arc::new(MobilePerformanceProfiler::new(config)?);
profiler.start_profiling()?;
let handles: Vec<_> = (0..5)
.map(|i| {
let profiler_clone = Arc::clone(&profiler);
thread::spawn(move || {
for j in 0..10 {
let event_name = format!("thread_{}_event_{}", i, j);
let _ = profiler_clone.record_inference_event(&event_name, Some(10.0));
let _ = profiler_clone.get_current_metrics();
let _ = profiler_clone.detect_bottlenecks();
}
})
})
.collect();
for handle in handles {
handle.join().expect("Thread panicked");
}
let profiling_data = profiler.stop_profiling()?;
assert!(!profiling_data.events.is_empty());
Ok(())
}
}