Skip to content

Sum CPU/memory of a collapsed subtree in tree view (#920)#2023

Open
pisanvs wants to merge 7 commits into
htop-dev:mainfrom
pisanvs:feature/collapsed-subtree-sum
Open

Sum CPU/memory of a collapsed subtree in tree view (#920)#2023
pisanvs wants to merge 7 commits into
htop-dev:mainfrom
pisanvs:feature/collapsed-subtree-sum

Conversation

@pisanvs

@pisanvs pisanvs commented Jun 16, 2026

Copy link
Copy Markdown

Implements #920. In tree view, when a node is collapsed, optionally display the sum of its subtree's additive values instead of only the node's own.

  • Opt-in, off by default: new Display Options toggle "Sum CPU/memory usage of collapsed subtrees" (htoprc key tree_sum_collapsed_subtree).
  • Summed fields: CPU%, normalized CPU%, MEM%, VIRT, RES, page faults, CPU time on all platforms; on Linux also memory sizes (PRIV/PSS/SWAP/…), IO bytes/counts/rates, context switches, delay-accounting %, and GPU. Non-additive fields (PID, STATE, USER, NICE, …) keep the node's own value.
  • Threads are excluded from the sum (they share their process's resources, already counted) — only child processes contribute, matching the request's intent.
  • Summed values render in a dim/tinted color (PROCESS_SUM, defaults to the shadow style per theme); the CPU% column auto-widens so large sums don't overflow.
  • Computed in O(n) during the existing bottom-up tree walk, gated entirely behind the setting (zero cost when off).

Verified on Linux: collapsing a parent of three ~95 MB / ~100%-CPU children shows RES ≈ 287 MB and CPU ≈ 300.8% (summed, dimmed); a process with N threads shows its own values, not N×.
Generated (mostly) with Claude Code

pisanvs and others added 7 commits June 15, 2026 18:42
New themeable color element used to tint values that represent the
total of a collapsed tree node's subtree. Defaults to the existing
PROCESS_SHADOW (dim) style in every bundled color scheme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Morel <maxmorel@pisanvs.cl>
Introduce the collapsedSubtreeSum setting (htoprc key
tree_sum_collapsed_subtree, off by default) and expose it as a
checkbox in the tree-view section of the Display Options screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Morel <maxmorel@pisanvs.cl>
Add the aggregateClear/aggregateAdd class methods (with no-op
fallbacks) and an 'aggregated' row flag, providing the generic
scaffolding the table builder uses to accumulate subtree totals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Morel <maxmorel@pisanvs.cl>
Store per-subtree totals of the additive fields shared by all
platforms (CPU%, normalized CPU%, MEM%, VIRT, RES, page faults, CPU
time) and render them, tinted, when a collapsed tree node displays
its subtree summary. Closes htop-dev#920.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Morel <maxmorel@pisanvs.cl>
Extend subtree summation to the Linux additive columns: memory sizes
(PRIV/PSS/SWAP/PSSWP/SHARE/TRS/DRS/LRS), self CPU times, IO bytes,
counts and rates, context switches, delay-accounting percentages and
GPU usage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Morel <maxmorel@pisanvs.cl>
Point each platform's process class at the shared aggregate hooks so
collapsed-subtree summation of the common fields works on every
platform, not just Linux.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Morel <maxmorel@pisanvs.cl>
When the setting is enabled in tree view, clear each node's aggregate
and fold in its children's totals during the existing bottom-up tree
walk, marking collapsed non-empty nodes so they render the subtree
summary. This activates the feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Morel <maxmorel@pisanvs.cl>
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an optional "Sum CPU/memory usage of collapsed subtrees" feature to htop's tree view. A new collapsedSubtreeSum boolean is added to Settings, persisted as tree_sum_collapsed_subtree, and exposed as a checkbox in the Display Options panel. The Row base type gains an aggregated flag and two optional vtable hooks (aggregateClear, aggregateAdd). Table_buildTreeBranch invokes these hooks to accumulate subtree totals into a ProcessAggregate (or LinuxProcessAggregate) struct embedded on the root row, then marks the root as aggregated. Process_writeField and LinuxProcess_rowWriteField detect this flag and substitute aggregate values for relevant numeric fields, recoloring output with a new PROCESS_SUM color element defined across all seven built-in color schemes. All platform-specific ProcessClass vtables are wired to the shared aggregation callbacks.

Poem

A tree folds closed, its children gone from sight,
Yet sums persist — their memory held in light.
ProcessAggregate keeps the tally true,
PROCESS_SUM recolors what the subtree grew.
One checkbox ticked; the collapsed rows confess
the total weight of all they still possess.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Table.c (1)

223-237: ⚠️ Potential issue | 🟠 Major

Clear aggregated flags when transitioning from tree to non-tree mode.

The non-tree branch of Table_updateDisplayList (lines 229-237) does not reset row->aggregated flags. When switching from tree to non-tree view, rows retain aggregated=true from the previous tree state, causing Process_writeField to render aggregate values (e.g., PERCENT_CPU, M_RESIDENT) instead of individual row values. Toggling treeView correctly sets needsSort=true, but the non-tree path never clears these flags because it skips Table_buildTree which would reset them.

Add a loop in the non-tree branch to clear all row->aggregated flags before rebuilding the display list, similar to how Table_buildTree does at line 182.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ccc4555a-68ce-41b0-8b71-713b23bb8faf

📥 Commits

Reviewing files that changed from the base of the PR and between e9518b3 and 9f26936.

📒 Files selected for processing (20)
  • CRT.c
  • CRT.h
  • DisplayOptionsPanel.c
  • Process.c
  • Process.h
  • Row.c
  • Row.h
  • Settings.c
  • Settings.h
  • Table.c
  • darwin/DarwinProcess.c
  • dragonflybsd/DragonFlyBSDProcess.c
  • freebsd/FreeBSDProcess.c
  • linux/LinuxProcess.c
  • linux/LinuxProcess.h
  • netbsd/NetBSDProcess.c
  • openbsd/OpenBSDProcess.c
  • pcp/PCPProcess.c
  • solaris/SolarisProcess.c
  • unsupported/UnsupportedProcess.c

Comment thread Process.c
Comment on lines +702 to +725
case MAJFLT: {
size_t start = RichString_size(str);
Row_printCount(str, useAgg ? this->aggregate.majflt : this->majflt, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
case MINFLT: {
size_t start = RichString_size(str);
Row_printCount(str, useAgg ? this->aggregate.minflt : this->minflt, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
case M_RESIDENT: {
size_t start = RichString_size(str);
Row_printKBytes(str, useAgg ? (unsigned long long)this->aggregate.m_resident : (unsigned long long)this->m_resident, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
case M_VIRT: {
size_t start = RichString_size(str);
Row_printKBytes(str, useAgg ? (unsigned long long)this->aggregate.m_virt : (unsigned long long)this->m_virt, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Move single-statement if bodies to the next line.

Several newly added branches keep the body on the same line as the condition (e.g., if (useAgg) ...). This violates the C style rule used in this repo.

Suggested style-only patch
-      if (useAgg) Process_aggregateRecolor(str, start);
+      if (useAgg)
+         Process_aggregateRecolor(str, start);

-      if (useAgg) attr = CRT_colors[PROCESS_SUM];
+      if (useAgg)
+         attr = CRT_colors[PROCESS_SUM];
As per coding guidelines, `**/*.c`: “never put control flow body on same line as condition”.

Also applies to: 743-756, 808-813

Source: Coding guidelines

@fasterit fasterit added the enhancement Extension or improvement to existing feature label Jun 16, 2026
@fasterit

Copy link
Copy Markdown
Member

Thank you.

Please clean up the color handling stuff (No re-coloring) and make logical commits.

Add a hotkey to switch the aggregate function on/off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Extension or improvement to existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants