Skip to content

darwin: add I/O screen tab with per-process disk I/O - #2133

Merged
BenBE merged 2 commits into
htop-dev:mainfrom
samm-git:darwin-io-tab
Oct 17, 2026
Merged

BenBE merged 2 commits into
htop-dev:mainfrom
samm-git:darwin-io-tab

Conversation

@samm-git

Copy link
Copy Markdown
Contributor

Description

Mirrors the Linux I/O tab on Darwin:

PID USER IO_RATE IO_READ_RATE IO_WRITE_RATE RBYTES WBYTES Command

macOS has no /proc/<pid>/io, but proc_pid_rusage(pid, RUSAGE_INFO_V2, ...)
provides ri_diskio_bytesread/ri_diskio_byteswritten. Rates are deltas
between scans, reusing the math and PROCESS_FLAG_IO gating from
LinuxProcessTable_readIoFile. Field ids/names/titles match Linux, so column
config stays portable.

Limitations

  • macOS exposes only cumulative byte counters: no RCHAR/WCHAR/SYSCR/
    SYSCW/CNCLWB, IO_PRIORITY, or PERCENT_*_DELAY.
  • proc_pid_rusage() for other users' processes needs root; unprivileged they
    show N/A (same ceiling as Linux without CAP_DAC_READ_SEARCH).
  • Per-thread rows show N/A (macOS tracks I/O per process).

Testing

  • Clean build; -Werror build of the changed files (Apple clang) on macOS.
  • Live: [I/O] tab populates and sorts by DISK R/W.

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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: ba9b8fba-8641-49ab-8077-84884c04aac5
📥 Commits

Reviewing files that changed from the base of the PR and between 1257b4a and 8aa9c05.

📒 Files selected for processing: 2
  • darwin/DarwinProcess.h
  • darwin/DarwinProcessTable.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

Darwin now collects per-process disk I/O byte counters and calculates read and write rates when I/O scanning is enabled. The process display exposes byte totals and individual and combined rates, supports sorting by these values, and includes a default I/O screen.

Suggested reviewers: natoscott

Priority: ⬇️ Low

Change: Feature

Merge Risk: ⚪ Minimal · up to 8aa9c

The previously identified sampling and default-sort issues are addressed. No actionable merge-blocking risk remains after normal checks.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Counters gather, rates take shape
Read and write find their place
Combined totals join the view
Columns sort as values change
Darwin shows the I/O flow

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: 2


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: d447a665-a82a-4ac2-a763-70ff2404b549
📥 Commits

Reviewing files that changed from the base of the PR and between 44a59cf and f3d72d4.

📒 Files selected for processing (5)
  • darwin/DarwinProcess.c
  • darwin/DarwinProcess.h
  • darwin/DarwinProcessTable.c
  • darwin/Platform.c
  • darwin/ProcessField.h

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

Comment thread darwin/DarwinProcess.c
Comment thread darwin/DarwinProcessTable.c Outdated
@samm-git

Copy link
Copy Markdown
Contributor Author

Addressed both findings in 5c52175:

  • Sort unavailable byte totals last (RBYTES/WBYTES): the ULLONG_MAX sentinel is now compared separately so unavailable rows sort last, consistent with how compareRealNumbers already orders N/A rates.
  • Valid baseline before computing rates: a rate is computed only once a previous valid sample exists, so the first scan (and the scan right after a failed one) reports N/A instead of a meaningless/zero delta.

Verified in a live macOS session: under disk load the rates show proper deltas (e.g. 2.17 G/s write bursts) and unavailable rows sort to the bottom.

@BenBE BenBE added MacOS 🍏 MacOS / Darwin related issues feature request Completely new feature requested labels Oct 13, 2026
@BenBE

BenBE commented Oct 16, 2026

Copy link
Copy Markdown
Member

Please split the commit into one providing the new columns and another one to add this new screen.

@samm-git

Copy link
Copy Markdown
Contributor Author

Split into two commits as requested:

  • 2cc3f3f darwin: add per-process disk I/O columns — field ids/struct, proc_pid_rusage gathering, rendering and sorting.
  • 1257b4a darwin: add I/O screen tab — registers the I/O screen using those columns.

No code changes; the resulting tree is identical to the previous commit.

Comment thread darwin/DarwinProcessTable.c
@samm-git

Copy link
Copy Markdown
Contributor Author

Good catch — fixed in ef642c2 (still split as two commits: columns, then screen).

The rate delta now uses host->monotonicMs instead of host->realtimeMs, so wall-clock adjustments (NTP/manual settimeofday) can no longer yield a zero or inflated time_delta. Also treats Machine_scanTables' first-scan monotonicMs == 1 sentinel as "no baseline yet", so the second sample cannot divide by a bogus interval.

Note: LinuxProcessTable_readIoFile uses host->realtimeMs for the same calculation, so it has the same theoretical issue. I left that alone to keep this PR Darwin-scoped — happy to follow up with a shared/monotonic fix for Linux if you want.

@BenBE

BenBE commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Good catch — fixed in ef642c2 (still split as two commits: columns, then screen).

The rate delta now uses host->monotonicMs instead of host->realtimeMs, so wall-clock adjustments (NTP/manual settimeofday) can no longer yield a zero or inflated time_delta. Also treats Machine_scanTables' first-scan monotonicMs == 1 sentinel as "no baseline yet", so the second sample cannot divide by a bogus interval.

Thx.

Note: LinuxProcessTable_readIoFile uses host->realtimeMs for the same calculation, so it has the same theoretical issue. I left that alone to keep this PR Darwin-scoped — happy to follow up with a shared/monotonic fix for Linux if you want.
Fine as a separate commit. As long as the core PR keeps centered on a core topic, it's still fine to fix issues it finds along the way.

BTW, the wording+style in your first commit message can be cleaned up slightly like this:

Add RBYTES, WBYTES, IO_READ_RATE, IO_WRITE_RATE, and IO_RATE process
columns on Darwin. macOS has no /proc/<pid>/io, but
proc_pid_rusage(pid, RUSAGE_INFO_V2, ...) provides
ri_diskio_bytesread/ri_diskio_byteswritten. Rates are derived from the
delta between consecutive scans, using the same formula and
PROCESS_FLAG_IO gating as LinuxProcessTable_readIoFile. Field IDs, names,
and titles match the Linux platform, so column configuration stays
portable.

macOS exposes only cumulative byte counters (no RCHAR/WCHAR/SYSCR/SYSCW/
CNCLWB, IO_PRIORITY, or PERCENT_*_DELAY), and the rusage of other users'
processes requires root privileges, so those show N/A when unprivileged.

Regarding RCHAR/WCHAR: That's characters read/written in total IIRC. While SYSCR and SYSCW is number of read(2)/write(2) syscalls. The difference for RBYTES vs. RCHAR and WBYTES vs. WCHAR is likely mostly one coming from the data source these are derived from. Would have to investigate those. Edit: See below.

CNCLWB (Cancelled Writtten Bytes) is the number of I/O requested, but cancelled later. That's likely specific to Linux and has little priority.

IO_PRIORITY is platform-specific, but usually couples with the overall process priority and determines, given a saturated I/O link, which process is allowed first when determining which data should be written next. This would for MacOS likely map to the concept of IO policies managed by getiopolicy_np() and setiopolicy_np().

The PERCENT_*_DELAY are the I/O delay accounting metrics, which provide information about how much time a process was waiting on its synchronous I/O to be completed. AFAICT there's no clean mapping of this on MacOS.

If you include these additional columns, it's fine (and slightly preferred) to have them as separate commits. For the Linux bugfix on monotonic vs. wallclock time, put that bugfix at the start or end of the patchset.

Edit: RCHAR/RBYTES and WCHAR/WBYTES: The *CHAR metrics tell the number of bytes requested by the process for reading/writing through syscalls like read,pread,… while the *BYTES metrics tell the number of bytes that actually hit the physical media backing the storage. If you had a file fully in cache, then RCHAR would increase for every read, while RBYTES would remain unchanged.

Add RBYTES, WBYTES, IO_READ_RATE, IO_WRITE_RATE, and IO_RATE process
columns on Darwin. macOS has no /proc/<pid>/io, but
proc_pid_rusage(pid, RUSAGE_INFO_V2, ...) provides
ri_diskio_bytesread/ri_diskio_byteswritten. Rates are derived from the
delta between consecutive scans, using the same formula and
PROCESS_FLAG_IO gating as LinuxProcessTable_readIoFile. Field IDs, names,
and titles match the Linux platform, so column configuration stays
portable.

macOS exposes only cumulative byte counters (no RCHAR/WCHAR/SYSCR/SYSCW/
CNCLWB, IO_PRIORITY, or PERCENT_*_DELAY), and the rusage of other users'
processes requires root privileges, so those show N/A when unprivileged.

The rate delta uses the monotonic clock, so wall-clock adjustments cannot
skew the result; LinuxProcessTable_readIoFile still uses the realtime
clock for this calculation (to be fixed separately).

Signed-off-by: Oleksij Samorukov <[email protected]>
Assisted-by: DeepSeek V4.1 Flash (opencode)
Add a second screen tab ("I/O") on Darwin, mirroring the Linux I/O tab,
showing the per-process disk I/O columns:

    PID USER IO_RATE IO_READ_RATE IO_WRITE_RATE RBYTES WBYTES Command

Signed-off-by: Oleksij Samorukov <[email protected]>
Assisted-by: DeepSeek V4.1 Flash (opencode)
@samm-git

Copy link
Copy Markdown
Contributor Author

Thanks — reworded the first commit message per your suggestion (now 19201a8). I kept a short extra paragraph noting that the delta uses the monotonic clock and that the equivalent Linux calculation still uses the realtime clock (to be fixed separately), since that change is part of this commit.

No code changes; the tree is identical.

@BenBE BenBE left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM.

@BenBE
BenBE merged commit 05c0af5 into htop-dev:main Oct 17, 2026
21 checks passed
@BenBE BenBE added this to the 3.6.0 milestone Oct 17, 2026
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 MacOS 🍏 MacOS / Darwin related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants