diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b17ee83..2b3ece1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -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, + is_syspulse: bool, } #[derive(Clone)] @@ -87,14 +88,29 @@ struct AggregatedProcess { instance_count: usize, warnings: Vec, history: Vec, + 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, - 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) -> Report { let mut peak_stats: HashMap = HashMap::new(); // (Peak CPU, Peak Mem) let mut unique_pids: HashMap> = HashMap::new(); let mut status_flags: HashMap = HashMap::new(); // Zombie check + let mut syspulse_flags: HashMap = HashMap::new(); for snapshot in &profiling.snapshots { let mut snapshot_procs: HashMap = HashMap::new(); @@ -231,6 +250,9 @@ fn stop_profiling(state: State) -> 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) -> 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) -> Report { instance_count: count, warnings, history, + is_syspulse, }); } diff --git a/src/App.tsx b/src/App.tsx index 750a2a5..3634a6e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,6 +20,7 @@ interface ProcessStats { memory: number; status: string; user_id?: string; + is_syspulse: boolean; } interface SystemStats { @@ -52,6 +53,7 @@ interface AggregatedProcess { instance_count: number; warnings: string[]; history: ProcessHistoryPoint[]; + is_syspulse: boolean; } interface ProfilingReport { @@ -77,7 +79,6 @@ function App() { try { const isRecording = stats?.is_recording ?? false; const data = await invoke('get_system_stats', { - excludeSelf: true, minimal: isRecording || view === 'report' }); setStats(data); @@ -289,7 +290,10 @@ function App() { {stats.processes.map((proc) => (
-
+
{proc.name}
{proc.pid}
@@ -321,6 +325,7 @@ function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () => const [sortField, setSortField] = useState('avg_cpu'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); const [selectedProcess, setSelectedProcess] = useState(null); + const [hideProfiler, setHideProfiler] = useState(true); const handleSort = (field: SortField) => { if (sortField === field) { @@ -332,20 +337,22 @@ function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () => }; const sortedProcesses = useMemo(() => { - return [...report.aggregated_processes].sort((a, b) => { - const valA = a[sortField as keyof AggregatedProcess]; - const valB = b[sortField as keyof AggregatedProcess]; - - if (typeof valA === 'string' && typeof valB === 'string') { - return sortOrder === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA); - } - - const numA = (valA as number) ?? 0; - const numB = (valB as number) ?? 0; - - return sortOrder === 'asc' ? numA - numB : numB - numA; - }); - }, [report, sortField, sortOrder]); + return [...report.aggregated_processes] + .filter(p => !hideProfiler || !p.is_syspulse) + .sort((a, b) => { + const valA = a[sortField as keyof AggregatedProcess]; + const valB = b[sortField as keyof AggregatedProcess]; + + if (typeof valA === 'string' && typeof valB === 'string') { + return sortOrder === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA); + } + + const numA = (valA as number) ?? 0; + const numB = (valB as number) ?? 0; + + return sortOrder === 'asc' ? numA - numB : numB - numA; + }); + }, [report, sortField, sortOrder, hideProfiler]); const saveReport = async () => { try { @@ -366,6 +373,17 @@ function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () => Profiling Report
+