feat: implement collapsible tree view with inclusive resource metrics and improved memory calculation

This commit is contained in:
2026-02-22 23:33:19 +01:00
parent d0398edb29
commit 6b2a851c3e
2 changed files with 24 additions and 17 deletions

View File

@@ -3,17 +3,17 @@ name = "syspulse-rs"
version = "0.1.0" version = "0.1.0"
description = "A professional Linux system profiler" description = "A professional Linux system profiler"
authors = ["narl"] authors = ["narl"]
edition = "2021" edition = "2024"
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.5.5" } tauri-build = { version = "2.5.5" }
[dependencies] [dependencies]
tauri = { version = "2.10.2", features = [] } chrono = "0.4.43"
env_logger = "0.11.9"
log = "0.4.29"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149" serde_json = "1.0.149"
sysinfo = "0.38.2" sysinfo = "0.38.2"
chrono = "0.4.43" tauri = "2.10.2"
tokio = { version = "1.49.0", features = ["full"] } tokio = { version = "1.49.0", features = ["full"] }
log = "0.4.29"
env_logger = "0.11.9"

View File

@@ -241,7 +241,6 @@ fn stop_profiling(state: State<AppState>) -> Report {
// 2. Aggregate RAW stats per PID // 2. Aggregate RAW stats per PID
struct PidStats { struct PidStats {
name: String, name: String,
parent_pid: Option<u32>,
history: Vec<ProcessHistoryPoint>, history: Vec<ProcessHistoryPoint>,
peak_cpu: f32, peak_cpu: f32,
peak_mem: f32, peak_mem: f32,
@@ -256,7 +255,6 @@ fn stop_profiling(state: State<AppState>) -> Report {
for proc in &snapshot.processes { for proc in &snapshot.processes {
let entry = pid_map.entry(proc.pid).or_insert_with(|| PidStats { let entry = pid_map.entry(proc.pid).or_insert_with(|| PidStats {
name: proc.name.clone(), name: proc.name.clone(),
parent_pid: proc.parent_pid,
history: Vec::new(), history: Vec::new(),
peak_cpu: 0.0, peak_cpu: 0.0,
peak_mem: 0.0, peak_mem: 0.0,
@@ -320,7 +318,7 @@ fn stop_profiling(state: State<AppState>) -> Report {
let pids: Vec<u32> = nodes.keys().cloned().collect(); let pids: Vec<u32> = nodes.keys().cloned().collect();
for pid in pids { for pid in pids {
if let Some(&ppid) = child_to_parent.get(&pid) { if let Some(&_ppid) = child_to_parent.get(&pid) {
// Already handled in recursive aggregation or linked below // Already handled in recursive aggregation or linked below
} else { } else {
root_pids.push(pid); root_pids.push(pid);
@@ -328,23 +326,24 @@ fn stop_profiling(state: State<AppState>) -> Report {
} }
// 5. Recursive function to calculate inclusive stats and build tree // 5. Recursive function to calculate inclusive stats and build tree
fn build_node(pid: u32, nodes: &mut HashMap<u32, AggregatedProcess>, child_map: &HashMap<u32, Vec<u32>>) -> AggregatedProcess { fn build_node(pid: u32, nodes: &mut HashMap<u32, AggregatedProcess>, child_map: &HashMap<u32, Vec<u32>>) -> Option<AggregatedProcess> {
let mut node = nodes.remove(&pid).unwrap(); let mut node = nodes.remove(&pid)?;
let children_pids = child_map.get(&pid).cloned().unwrap_or_default(); let children_pids = child_map.get(&pid).cloned().unwrap_or_default();
let mut inc_cpu = node.avg_cpu; let mut inc_cpu = node.avg_cpu;
let mut inc_mem = node.avg_memory_mb; let mut inc_mem = node.avg_memory_mb;
for c_pid in children_pids { for c_pid in children_pids {
let child_node = build_node(c_pid, nodes, child_map); if let Some(child_node) = build_node(c_pid, nodes, child_map) {
inc_cpu += child_node.inclusive_avg_cpu; inc_cpu += child_node.inclusive_avg_cpu;
inc_mem += child_node.inclusive_avg_memory_mb; inc_mem += child_node.inclusive_avg_memory_mb;
node.children.push(child_node); node.children.push(child_node);
} }
}
node.inclusive_avg_cpu = inc_cpu; node.inclusive_avg_cpu = inc_cpu;
node.inclusive_avg_memory_mb = inc_mem; node.inclusive_avg_memory_mb = inc_mem;
node Some(node)
} }
let mut child_map: HashMap<u32, Vec<u32>> = HashMap::new(); let mut child_map: HashMap<u32, Vec<u32>> = HashMap::new();
@@ -354,8 +353,16 @@ fn stop_profiling(state: State<AppState>) -> Report {
let mut final_roots = Vec::new(); let mut final_roots = Vec::new();
for pid in root_pids { for pid in root_pids {
if nodes.contains_key(&pid) { if let Some(root_node) = build_node(pid, &mut nodes, &child_map) {
final_roots.push(build_node(pid, &mut nodes, &child_map)); final_roots.push(root_node);
}
}
// Include any remaining orphan nodes as roots (e.g. if parent info was missing in snapshots)
let remaining_pids: Vec<u32> = nodes.keys().cloned().collect();
for pid in remaining_pids {
if let Some(node) = build_node(pid, &mut nodes, &child_map) {
final_roots.push(node);
} }
} }