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

@@ -241,7 +241,6 @@ fn stop_profiling(state: State<AppState>) -> Report {
// 2. Aggregate RAW stats per PID
struct PidStats {
name: String,
parent_pid: Option<u32>,
history: Vec<ProcessHistoryPoint>,
peak_cpu: f32,
peak_mem: f32,
@@ -256,7 +255,6 @@ fn stop_profiling(state: State<AppState>) -> Report {
for proc in &snapshot.processes {
let entry = pid_map.entry(proc.pid).or_insert_with(|| PidStats {
name: proc.name.clone(),
parent_pid: proc.parent_pid,
history: Vec::new(),
peak_cpu: 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();
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
} else {
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
fn build_node(pid: u32, nodes: &mut HashMap<u32, AggregatedProcess>, child_map: &HashMap<u32, Vec<u32>>) -> AggregatedProcess {
let mut node = nodes.remove(&pid).unwrap();
fn build_node(pid: u32, nodes: &mut HashMap<u32, AggregatedProcess>, child_map: &HashMap<u32, Vec<u32>>) -> Option<AggregatedProcess> {
let mut node = nodes.remove(&pid)?;
let children_pids = child_map.get(&pid).cloned().unwrap_or_default();
let mut inc_cpu = node.avg_cpu;
let mut inc_mem = node.avg_memory_mb;
for c_pid in children_pids {
let child_node = build_node(c_pid, nodes, child_map);
inc_cpu += child_node.inclusive_avg_cpu;
inc_mem += child_node.inclusive_avg_memory_mb;
node.children.push(child_node);
if let Some(child_node) = build_node(c_pid, nodes, child_map) {
inc_cpu += child_node.inclusive_avg_cpu;
inc_mem += child_node.inclusive_avg_memory_mb;
node.children.push(child_node);
}
}
node.inclusive_avg_cpu = inc_cpu;
node.inclusive_avg_memory_mb = inc_mem;
node
Some(node)
}
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();
for pid in root_pids {
if nodes.contains_key(&pid) {
final_roots.push(build_node(pid, &mut nodes, &child_map));
if let Some(root_node) = 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);
}
}