Skip to content

Add hide_offline_cpus setting to hide offline CPUs in the CPU meters - #2104

Merged
fasterit merged 3 commits into
htop-dev:mainfrom
Edznux:hide-offline-cpus
Oct 2, 2026
Merged

fasterit merged 3 commits into
htop-dev:mainfrom
Edznux:hide-offline-cpus

Conversation

@Edznux

@Edznux Edznux commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Hello :)

Context

I was working on a relatively large server where we disabled SMT and this caused the 64 core / 128 threads listing to have 64 [offline] CPU displayed. This was less than ideal when using a small terminal pane, especially to see the load on the other 64 cores, so I decided to take a look at it. I noticed #853 and decided to pick it up!

I don't know the codebase, and I'm not a particularly good C dev, but I've reviewed the code and steered my agent for a bit to achieve this. I do believe it went a bit too far with some changes, but I also think it made sense for the purpose of the PR.

I do believe I've followed the contribution guidance and the AI disclosure and all as well, but happy to change.

Goal

On machines booted with nosmt (or with CPUs otherwise offlined), the multi-CPU meters draw a bar reading "offline" for every disabled thread, doubling the height of the header on large SMT systems for no benefit.

Add a "Hide offline CPUs in the CPUs meters" display option (htoprc key hide_offline_cpus, default off).
When enabled, the CPUs meters lay out the sequence of online CPUs instead of every existing CPU, so the first/second half and multi-column layouts split over the shown CPUs only. Sub-meters are now kept per CPU id and created when a CPU is first shown, so a CPU that is hidden or shifts to another slot keeps its graph history; the meter recomputes its own height whenever the shown sequence changes.

Header_updateData now recalculates the header height and reports whether it changed, so the refresh loop re-lays out the screen when CPUs go online or offline at runtime instead of leaving blank or clipped rows until the terminal is resized.

Recorded run

Here's an example of how it looks on my 16C/32T machine, with a script that disables cores over time.

htop-issue-853-implem-crf24.mp4

Closes #853

Assisted-by: Claude Fable 5.1 noreply@anthropic.com

On machines booted with nosmt (or with CPUs otherwise offlined), the
multi-CPU meters draw a bar reading "offline" for every disabled thread,
doubling the height of the header on large SMT systems for no benefit.

Add a "Hide offline CPUs in the CPUs meters" display option (htoprc key
hide_offline_cpus, default off). When enabled, the CPUs meters lay out
the sequence of online CPUs instead of every existing CPU, so the
first/second half and multi-column layouts split over the shown CPUs
only. Sub-meters are now kept per CPU id and created when a CPU is
first shown, so a CPU that is hidden or shifts to another slot keeps
its graph history; the meter recomputes its own height whenever the
shown sequence changes.

Header_updateData now recalculates the header height and reports
whether it changed, so the refresh loop re-lays out the screen when
CPUs go online or offline at runtime instead of leaving blank or
clipped rows until the terminal is resized.

Closes htop-dev#853

Assisted-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Edouard Schweisguth <edznux@gmail.com>
@coderabbitai

coderabbitai Bot commented Sep 11, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: f166387d-4ce4-4c88-88f2-3f269eb68395

📥 Commits

Reviewing files that changed from the base of the PR and between f0ea593 and 4bcc8d7.

📒 Files selected for processing (1)
  • CPUMeter.c

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Adds a persistent hide_offline_cpus setting and a display-panel checkbox. CPU meters now filter offline CPUs, map shown CPUs into shared sub-meters, and support shared lifecycle and drawing logic across column variants. Header updates now report height changes so ScreenManager can resize panels and request redraws only when required.

Assessment against linked issues:

Objective Addressed Explanation
Add a setup or runtime option to hide offline CPUs in CPU meters [#853] ✅

Suggested reviewers: benbe

Priority: ⬇️ Low

Change: Feature · Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to 4bcc8

The option is off by default, and reported tests covered CPU filtering, split layouts, and hot-plug redraws. No merge blocker is evident.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 4bcc8

The reviewed paths affect interactive CPU display and do not show a new privileged operation or security boundary. Dynamic CPU changes and incomplete coverage leave some residual uncertainty.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — In the examined flow, the setting and CPU-online state affect which local meters are displayed and how panels are sized; no path from them to a privileged or cross-tenant sink was established.

Trust Boundaries and Controls

  • inferred — The flagged helper does not expand the exposed boundary: it remains static and is called by the meter's internal initialization path, after CPU selection.

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

Offline bars fade from view
Shared meters line up anew
Settings hold the chosen state
Headers resize at the proper rate
CPU columns draw clean and true

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

@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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 90e2a14a-af2b-4327-8129-d7dff80b6645

📥 Commits

Reviewing files that changed from the base of the PR and between 07fbb8d and 8fae1fc.

📒 Files selected for processing (7)
  • CPUMeter.c
  • DisplayOptionsPanel.c
  • Header.c
  • Header.h
  • ScreenManager.c
  • Settings.c
  • Settings.h

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment thread CPUMeter.c Outdated
Follow the ModuleName_functionName() convention from the style guide
for the static helpers shared by the CPUs meters.

Assisted-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Edouard Schweisguth <edznux@gmail.com>
@BenBE BenBE added the feature request Completely new feature requested label Sep 12, 2026

@ravi-arnan ravi-arnan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed this closely since I did the adjacent CPU hotplug fix (#2064). Fetched the PR head (f0ea593) and built on Linux under htop's own warning set: exit 0, zero new warnings in any touched file. Also ran the built binary for a few seconds of live refreshes under a pty with asserts enabled: no crash, no assert trip. All 16 CPUs on my machine are online, so the hide path itself is compile-verified plus traced by hand, not runtime-tested; stating that boundary explicitly.

Things I verified that hold up:

  • The per-CPU-id meters[] indexing subsumes the mid-range removal case from #2064: arbitrary online/offline gaps are handled by construction, and the Left/Right halves now partition the shown sequence, which is the sane semantic with hiding on.
  • Machine_isCPUonline() exists on all 8 platform backends, so the new gate is portable with no extra ifdefs.
  • Header_updateData() void-to-bool is source-compatible with the two other callers (DisplayOptionsPanel.c:235, AvailableMetersPanel.c:86); both use it as a bare statement.
  • AllCPUsMeter_updateMode() calls Meter_init(), which is only a ->init dispatch (Meter.h), so there is no updateMode/init recursion; mode propagation to the sub-meters through commonInitSubMeters is correct.
  • The xReallocArray growth path for shown cannot expose uninitialized slots: when the array grows, changed is true and the compare short-circuits.
  • Bonus this brings along: Header_updateData reporting height changes up to ScreenManager_resize means a live hotplug while htop runs now resizes the header instead of leaving a stale height.

One nit (non-blocking): the settings label says "(e.g. SMT siblings disabled by nosmt)", but offline CPUs equally come from hot-unplug. Suggest widening it, e.g. "(e.g. disabled by nosmt/nosmp or hot-unplugged)".

One question: hidden CPUs stop receiving Meter_updateValues(), so when a CPU comes back the first delta spans the whole hidden interval. For CPU% that reads as an average over the gap, which seems fine, but is that the intended semantic, or should a re-shown CPU reset its history instead?

No blocking issues found. Leaving approval to the maintainers.

@Edznux

Edznux commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

@ravi-arnan you can test by temporarily disabling a core
For example, disabling core 12:

sudo sh -c 'echo 0 > /sys/devices/system/cpu/cpu12/online'

and set to 1 to re-enable it :)

@ravi-arnan

Copy link
Copy Markdown
Contributor

Follow-up on the testing boundary I flagged in my review: I took a real CPU offline
and ran the hide path, so it is runtime-verified rather than traced by hand.

Method. CPU 12 offline through the kernel (echo 0 > /sys/devices/system/cpu/cpu12/online,
back to 1 after every run; the box is back to 16/16). Rebuilt at f0ea593 with
--enable-debug, so assertions are actually live this time. The binary ran in a pty
and I read the rendered screen after it settled.

Results with cpu12 offline:

  • hide=1, CPUs (1/1): 15 rows, 0 1 ... 11 13 14 15. cpu12 gone, the rest in place,
    and the header is exactly one row shorter (the process list moves up from line 19 to
    18). No assert trip, no crash.
  • hide=0: 16 rows, cpu12 still there as 12[ offline]. So the
    setting only controls visibility; the offline marking is untouched.
  • hide=1, CPUs (1/2) + (2/2): 8 + 7 rows. I put a meter between the two halves to make
    the boundary unambiguous: left = 0-7, right = 8, 9, 10, 11, 13, 14, 15. The halves
    partition the shown sequence, cpu12 is absent from both, and no slot is duplicated or
    dropped. This is the case I care about, since linux: release CPU data when a CPU is hot-unplugged #2064 was about a mid-range offline CPU and
    this is exactly what the shown-index mapping has to get right.
  • hide=1, CPUs (1&2/2): the 15 shown slots fill 8 rows in two columns
    (0-7 | 8-11,13-15), column-major, no hole where cpu12 would be.

Live hotplug in one instance (hide=1): started with cpu12 offline (15 rows), brought
it online (16 rows, cpu12 back, header one row taller), took it offline again (15 rows),
brought it back online (16). Four transitions in a single htop, no assert, no redraw
artifacts. That exercises Header_updateData() reporting the height change and the
ScreenManager_resize() behind it, which I could only check by reading the code last time.

And the answer to my own question (does a re-shown CPU average over the hidden gap):
no. The per-CPU periods are recomputed on every scan in LinuxMachine_scanCPUData(), not
in the meter, so hiding a meter does not stretch the next delta. There is a separate
pre-existing quirk for CPUs that are genuinely offline: LinuxMachine.c:580-585 (upstream,
from #2017) zeroes cpuData[i] for any id missing from /proc/stat, so the first value
after a CPU returns is measured against a zero baseline, one frame of "average since
boot". Not introduced here and it happens with hide=0 too, where the row is simply
visible; in the run cpu12 came back at 0.0% and looked unremarkable. Meter graph history
does freeze while hidden, so a re-shown graph has a time discontinuity, which seems
acceptable.

The label nit from the review still stands if you want it (the setup text mentions nosmt
but not nosmp/hot-unplug). Nothing blocking from me; approval to the maintainers as
before.

@fasterit

Copy link
Copy Markdown
Member
CPUMeter.c:332:12: warning: Access to field 'h' results in a dereference of a null pointer [core.NullDereference]
  332 |    int h = data->shown[0]->h;
      |            ^~~~~~~~~~~~~~~~~
1 warning generated.

from the clang analyzer CI job

The clang static analyzer cannot tell that the shown sub-meters are
never NULL (they come from Meter_new(), defined in another translation
unit) and reports a NULL dereference in CPUMeter_commonUpdateHeight():

  CPUMeter.c:332:12: warning: Access to field 'h' results in a
  dereference of a null pointer [core.NullDereference]

Fall back to a height of 1 when the first shown sub-meter is NULL,
like when no CPU is shown.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
@Edznux

Edznux commented Sep 27, 2026

Copy link
Copy Markdown
Contributor Author

Just added a null check! let me know :)

@ravi-arnan

Copy link
Copy Markdown
Contributor

Verified the null-deref fix at 4bcc8d7 with clang 21.1.8's analyzer, the same
core.NullDereference check the CI job runs:

  • f0ea593 (parent): warning reproduced at CPUMeter.c:332 —
    Access to field 'h' results in a dereference of a null pointer.
  • 4bcc8d7 (head): clean, no core.NullDereference.

Full build at head under htop's warning set
(-Wall -Wextra -Wcast-align ... -Wnull-dereference): exit 0, no warnings, binary links.

I also agree it is a false positive rather than a real hazard: whenever
shownCount > 0, CPUMeter_commonMapCPUs() has just (re)written every slot
0..count-1 from data->meters[], and those are created by Meter_new()
(xMalloc, aborts on OOM), so shown[0] is never NULL; shownCount > 0 also
implies shown != NULL. commonUpdateHeight() is only reached right after that
mapping. The new branch is therefore unreachable by construction and just falls
back to h = 1, the same as the no-shown-CPU case — harmless.

Nothing blocking from me; the earlier label nit (nosmt vs nosmp/hot-unplug)
still stands if you want it.

@fasterit fasterit added this to the 3.6.0 milestone Oct 2, 2026
@fasterit
fasterit merged commit d76b23e into htop-dev:main Oct 2, 2026
21 checks passed
@fasterit

fasterit commented Oct 2, 2026

Copy link
Copy Markdown
Member

Thank you both!

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

Labels

feature request Completely new feature requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Request for a setup option to hide offline CPUs in the CPUMeter

4 participants