use super::perfetto_parser::PerfettoTrace;
use perfetto_protos::ftrace_event::ftrace_event;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub struct CpuFrequencyAnalyzer;
impl CpuFrequencyAnalyzer {
pub fn analyze(trace: &PerfettoTrace) -> CpuFrequencyResult {
let mut per_cpu_freq: HashMap<u32, Vec<FrequencyEvent>> = HashMap::new();
let mut per_cpu_current_freq: HashMap<u32, u32> = HashMap::new();
for cpu in 0..trace.num_cpus() {
let events = trace.get_events_by_cpu(cpu as u32);
for event_with_idx in events {
if let Some(ftrace_event::Event::CpuFrequency(freq)) = &event_with_idx.event.event {
if let (Some(state), Some(ts)) = (freq.state, event_with_idx.event.timestamp) {
let freq_khz = state;
let event = FrequencyEvent {
cpu: cpu as u32,
frequency_khz: freq_khz,
timestamp: ts,
};
per_cpu_freq.entry(cpu as u32).or_default().push(event);
per_cpu_current_freq.insert(cpu as u32, freq_khz);
}
}
}
}
let mut cpu_stats: Vec<CpuFrequencyStats> = Vec::new();
for (cpu, events) in &per_cpu_freq {
if events.is_empty() {
continue;
}
let mut freq_durations: HashMap<u32, u64> = HashMap::new();
let mut frequencies: Vec<u32> = Vec::new();
for i in 0..events.len() {
let current = &events[i];
frequencies.push(current.frequency_khz);
if i + 1 < events.len() {
let next = &events[i + 1];
let duration = next.timestamp - current.timestamp;
*freq_durations.entry(current.frequency_khz).or_insert(0) += duration;
}
}
let min_freq = frequencies.iter().min().copied().unwrap_or(0);
let max_freq = frequencies.iter().max().copied().unwrap_or(0);
let avg_freq = if !frequencies.is_empty() {
frequencies.iter().sum::<u32>() as f64 / frequencies.len() as f64
} else {
0.0
};
let most_common_freq = freq_durations
.iter()
.max_by_key(|(_, duration)| *duration)
.map(|(freq, _)| *freq)
.unwrap_or(0);
cpu_stats.push(CpuFrequencyStats {
cpu: *cpu,
transition_count: events.len(),
min_frequency_khz: min_freq,
max_frequency_khz: max_freq,
avg_frequency_khz: avg_freq,
most_common_frequency_khz: most_common_freq,
freq_durations,
});
}
cpu_stats.sort_by_key(|s| s.cpu);
CpuFrequencyResult {
total_transitions: per_cpu_freq.values().map(|v| v.len()).sum(),
cpu_stats,
}
}
}
pub struct CpuIdleStateAnalyzer;
impl CpuIdleStateAnalyzer {
pub fn analyze(trace: &PerfettoTrace) -> CpuIdleResult {
let mut per_cpu_idle: HashMap<u32, Vec<IdleEvent>> = HashMap::new();
for cpu in 0..trace.num_cpus() {
let events = trace.get_events_by_cpu(cpu as u32);
for event_with_idx in events {
if let Some(ftrace_event::Event::CpuIdle(idle)) = &event_with_idx.event.event {
if let (Some(state), Some(ts)) = (idle.state, event_with_idx.event.timestamp) {
let event = IdleEvent {
cpu: cpu as u32,
state: state as i32,
timestamp: ts,
};
per_cpu_idle.entry(cpu as u32).or_default().push(event);
}
}
}
}
let mut cpu_stats: Vec<CpuIdleStats> = Vec::new();
for (cpu, events) in &per_cpu_idle {
if events.is_empty() {
continue;
}
let mut state_durations: HashMap<i32, u64> = HashMap::new();
let mut active_time = 0u64;
let mut idle_time = 0u64;
for i in 0..events.len() {
let current = &events[i];
if i + 1 < events.len() {
let next = &events[i + 1];
let duration = next.timestamp - current.timestamp;
if current.state == -1 || current.state == 4294967295u32 as i32 {
active_time += duration;
} else {
idle_time += duration;
*state_durations.entry(current.state).or_insert(0) += duration;
}
}
}
let total_time = active_time + idle_time;
let idle_percentage = if total_time > 0 {
(idle_time as f64 / total_time as f64) * 100.0
} else {
0.0
};
cpu_stats.push(CpuIdleStats {
cpu: *cpu,
transition_count: events.len(),
active_time_ns: active_time,
idle_time_ns: idle_time,
idle_percentage,
state_durations,
});
}
cpu_stats.sort_by_key(|s| s.cpu);
CpuIdleResult {
total_transitions: per_cpu_idle.values().map(|v| v.len()).sum(),
cpu_stats,
}
}
}
pub struct PowerStateAnalyzer;
impl PowerStateAnalyzer {
pub fn analyze(trace: &PerfettoTrace) -> PowerStateResult {
let mut suspend_resume_events: Vec<SuspendResumeEvent> = Vec::new();
for cpu in 0..trace.num_cpus() {
let events = trace.get_events_by_cpu(cpu as u32);
for event_with_idx in events {
if let Some(ftrace_event::Event::SuspendResume(sr)) = &event_with_idx.event.event {
if let Some(ts) = event_with_idx.event.timestamp {
let event = SuspendResumeEvent {
action: sr.action.clone().unwrap_or_default(),
timestamp: ts,
};
suspend_resume_events.push(event);
}
}
}
}
suspend_resume_events.sort_by_key(|e| e.timestamp);
PowerStateResult {
suspend_resume_count: suspend_resume_events.len(),
suspend_resume_events,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrequencyEvent {
pub cpu: u32,
pub frequency_khz: u32,
pub timestamp: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuFrequencyStats {
pub cpu: u32,
pub transition_count: usize,
pub min_frequency_khz: u32,
pub max_frequency_khz: u32,
pub avg_frequency_khz: f64,
pub most_common_frequency_khz: u32,
pub freq_durations: HashMap<u32, u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuFrequencyResult {
pub total_transitions: usize,
pub cpu_stats: Vec<CpuFrequencyStats>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdleEvent {
pub cpu: u32,
pub state: i32, pub timestamp: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuIdleStats {
pub cpu: u32,
pub transition_count: usize,
pub active_time_ns: u64,
pub idle_time_ns: u64,
pub idle_percentage: f64,
pub state_durations: HashMap<i32, u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuIdleResult {
pub total_transitions: usize,
pub cpu_stats: Vec<CpuIdleStats>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuspendResumeEvent {
pub action: String,
pub timestamp: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PowerStateResult {
pub suspend_resume_count: usize,
pub suspend_resume_events: Vec<SuspendResumeEvent>,
}
#[cfg(test)]
mod tests {
#[test]
fn test_cpu_frequency_analyzer_empty() {
}
#[test]
fn test_cpu_idle_analyzer_empty() {
}
#[test]
fn test_power_state_analyzer_empty() {
}
}