feat: implement automatic SysPulse detection and Report View overhead toggle

This commit is contained in:
2026-02-22 22:20:04 +01:00
parent 214a24be9d
commit ab4b2af800
2 changed files with 64 additions and 22 deletions

View File

@@ -3,7 +3,7 @@
windows_subsystem = "windows"
)]
use sysinfo::System;
use sysinfo::{System, Pid};
use std::sync::Mutex;
use std::process::Command;
use tauri::State;
@@ -31,6 +31,7 @@ struct ProcessStats {
memory: u64,
status: String,
user_id: Option<String>,
is_syspulse: bool,
}
#[derive(Clone)]
@@ -87,14 +88,29 @@ struct AggregatedProcess {
instance_count: usize,
warnings: Vec<String>,
history: Vec<ProcessHistoryPoint>,
is_syspulse: bool,
}
// --- Commands ---
fn is_descendant_of(pid: u32, target_pid: u32, sys: &System) -> bool {
let mut current_pid = Some(Pid::from_u32(pid));
while let Some(p) = current_pid {
if p.as_u32() == target_pid {
return true;
}
if let Some(process) = sys.process(p) {
current_pid = process.parent();
} else {
break;
}
}
false
}
#[tauri::command]
fn get_system_stats(
state: State<AppState>,
exclude_self: bool,
minimal: bool
) -> SystemStats {
let mut sys = state.sys.lock().unwrap();
@@ -116,15 +132,16 @@ fn get_system_stats(
Vec::new()
} else {
sys.processes().iter()
.filter(|(pid, _)| !exclude_self || pid.as_u32() != self_pid)
.map(|(pid, process)| {
let pid_u32 = pid.as_u32();
ProcessStats {
pid: pid.as_u32(),
pid: pid_u32,
name: process.name().to_string_lossy().to_string(),
cpu_usage: process.cpu_usage(),
memory: process.memory(),
status: format!("{:?}", process.status()),
user_id: process.user_id().map(|uid| uid.to_string()),
is_syspulse: is_descendant_of(pid_u32, self_pid, &sys),
}
}).collect()
};
@@ -134,15 +151,16 @@ fn get_system_stats(
if minimal {
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
processes = sys.processes().iter()
.filter(|(pid, _)| !exclude_self || pid.as_u32() != self_pid)
.map(|(pid, process)| {
let pid_u32 = pid.as_u32();
ProcessStats {
pid: pid.as_u32(),
pid: pid_u32,
name: process.name().to_string_lossy().to_string(),
cpu_usage: process.cpu_usage(),
memory: process.memory(),
status: format!("{:?}", process.status()),
user_id: process.user_id().map(|uid| uid.to_string()),
is_syspulse: is_descendant_of(pid_u32, self_pid, &sys),
}
}).collect();
}
@@ -218,6 +236,7 @@ fn stop_profiling(state: State<AppState>) -> Report {
let mut peak_stats: HashMap<String, (f32, f32)> = HashMap::new(); // (Peak CPU, Peak Mem)
let mut unique_pids: HashMap<String, std::collections::HashSet<u32>> = HashMap::new();
let mut status_flags: HashMap<String, bool> = HashMap::new(); // Zombie check
let mut syspulse_flags: HashMap<String, bool> = HashMap::new();
for snapshot in &profiling.snapshots {
let mut snapshot_procs: HashMap<String, (f32, u64)> = HashMap::new();
@@ -231,6 +250,9 @@ fn stop_profiling(state: State<AppState>) -> Report {
if proc.status.contains("Zombie") {
status_flags.insert(proc.name.clone(), true);
}
if proc.is_syspulse {
syspulse_flags.insert(proc.name.clone(), true);
}
}
// Record history for all processes seen in this snapshot
@@ -256,6 +278,7 @@ fn stop_profiling(state: State<AppState>) -> Report {
for (name, history) in process_map {
let (peak_cpu, peak_mem) = peak_stats.get(&name).cloned().unwrap_or((0.0, 0.0));
let count = unique_pids.get(&name).map(|s| s.len()).unwrap_or(0);
let is_syspulse = syspulse_flags.get(&name).cloned().unwrap_or(false);
// Average over the whole SESSION (zeros for snapshots where not present)
let total_cpu_sum: f32 = history.iter().map(|h| h.cpu_usage).sum();
@@ -284,6 +307,7 @@ fn stop_profiling(state: State<AppState>) -> Report {
instance_count: count,
warnings,
history,
is_syspulse,
});
}