Skip to content

linux: render N/A instead of 0 for unreadable smaps metrics - #2138

Open
ravi-arnan wants to merge 3 commits into
htop-dev:mainfrom
ravi-arnan:fix-1704-smaps-unknown
Open

ravi-arnan wants to merge 3 commits into
htop-dev:mainfrom
ravi-arnan:fix-1704-smaps-unknown

Conversation

@ravi-arnan

Copy link
Copy Markdown
Contributor

Closes #1704

Reading /proc/<pid>/smaps_rollup (or /proc/<pid>/smaps) requires PTRACE_MODE_READ. For processes owned by another user, an unprivileged caller gets EACCES.

In LinuxProcessTable_readSmapsFile(), when fopenat() returned NULL, the function returned false without modifying process->m_pss and friends. Because LinuxProcess is allocated with xCalloc(), those four fields (m_pss, m_swap, m_psswp, m_epss) retained their zero initialization. htop then rendered that 0 as if it were a valid measured value, while RES (from world-readable statm) was read normally.

0 is a measurement; "cannot read this" is not.

The change

  1. Widen types: Widened m_pss, m_swap, m_psswp, m_epss from long to unsigned long long in LinuxProcess.h (and matching PCPProcess.h), consistent with Row_printKBytes().
  2. Sentinel value: Initialized the four fields to ULLONG_MAX in LinuxProcess_new() (and PCPProcess_new()). When fopenat() or skipEndOfLine() fails in LinuxProcessTable_readSmapsFile(), the fields are explicitly set to ULLONG_MAX. Row_printKBytes() already renders ULLONG_MAX as shadowed N/A.
  3. Sorting: Added SPACESHIP_NUMBER_NA(a, b, na) in Macros.h so that N/A (ULLONG_MAX) is ranked lowest. Because M_PSS and friends have .defaultSortDesc = true, a plain maximum sentinel would float unreadable processes to the top when sorting descending. With SPACESHIP_NUMBER_NA, unreadable processes sink to the bottom on descending sort and float to the top only on ascending sort, matching how NaN / N/A behaves for percentage columns.
  4. Parsing: Changed strtol to strtoull in LinuxProcessTable_readSmapsFile() to match the widened unsigned type.

Verification

Tested on NixOS (Linux 6.17.0-35, glibc, unprivileged user ravi):

$ # PID 1 owned by root (unreadable smaps_rollup), PID 2327 owned by ravi
$ htop -p 1,2327

Before:

   PID USER         RES   PSS▽ SWAP  PSSWP  EPSS Command
     1 root       14224     0     0      0     0 /run/current-system/systemd/lib/systemd/systemd ...
  2327 ravi        2708   477     0      0   477 /nix/store/.../bin/sh ...

After (descending sort PSS▽):

   PID USER         RES   PSS▽ SWAP  PSSWP  EPSS Command
  2327 ravi        2708   477     0      0   477 /nix/store/.../bin/sh ...
     1 root       14224   N/A   N/A    N/A   N/A /run/current-system/systemd/lib/systemd/systemd ...

After (ascending sort PSS△):

   PID USER         RES   PSS△ SWAP  PSSWP  EPSS Command
     1 root       14224   N/A   N/A    N/A   N/A /run/current-system/systemd/lib/systemd/systemd ...
  2327 ravi        2708   477     0      0   477 /nix/store/.../bin/sh ...

Across all processes on descending sort, active memory consumers sort from largest to smallest, while all unreadable processes (N/A) sink to the bottom.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 06c863c3-65ab-4358-8a3f-724047e57ca0
📥 Commits

Reviewing files that changed from the base of the PR and between fcfa7c5 and cfe7fe6.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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: 8bd18563-9218-4380-9567-d95d8f8eb30b
📥 Commits

Reviewing files that changed from the base of the PR and between 344a131 and fcfa7c5.

📒 Files selected for processing: 1
  • linux/LinuxProcessTable.c

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

📝 Walkthrough

Linux and PCP process memory metrics now use unsigned values and ULLONG_MAX to represent unavailable data. Linux smaps parsing uses unsigned conversion and sets unavailable values when specified read steps fail. Sorting treats the sentinel as unavailable for the affected metrics.

Assessment against linked issues:

Objective Addressed Explanation
Fix the reported zero or incorrect PSS value [#1704] ✅

Suggested reviewers: benbe

Priority: ⬇️ Low

Change: Bug fix · Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to fcfa7

Unreadable smaps data now appears as N/A rather than a measured zero. The reviewed error paths set the unavailable sentinel, and no actionable risk remains for merge.

- [ ] 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

Unsigned values flow through the rows
A sentinel marks what no one knows
PSS finds its place in the line
Missing metrics sort by design
Linux and PCP keep the same sign

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.

Caution

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

⚠️ Outside diff range comments (1)

🟡 Minor · Mark metrics unavailable when fgets() fails. · LinuxProcessTable.c:895

linux/LinuxProcessTable.c:895
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Mark metrics unavailable when fgets() fails.

If a process exits after fopenat() succeeds, the Linux smaps_rollup reader can return ESRCH during the read. The loop then ends without checking ferror(fp). The fields retain zero or partial values instead of ULLONG_MAX. Check ferror(fp) after the loop and mark all four fields unavailable before closing the file. (raw.githubusercontent.com)


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: a8c6be93-9d2d-4344-a403-ae349bf3d0d2
📥 Commits

Reviewing files that changed from the base of the PR and between 05c0af5 and 344a131.

📒 Files selected for processing: 7
  • Macros.h
  • linux/LinuxProcess.c
  • linux/LinuxProcess.h
  • linux/LinuxProcessTable.c
  • pcp/PCPProcess.c
  • pcp/PCPProcess.h
  • pcp/PCPProcessTable.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.

@BenBE BenBE added enhancement Extension or improvement to existing feature Linux 🐧 Linux related issues PCP PCP related issues labels Oct 18, 2026
@BenBE

BenBE commented Oct 18, 2026

Copy link
Copy Markdown
Member

Please split the commit into the general changes, the ones affecting Linux and those related to PCP.

@ravi-arnan
ravi-arnan force-pushed the fix-1704-smaps-unknown branch from 344a131 to fcfa7c5 Compare October 18, 2026 17:19
…west

When a metric is unavailable (represented by a sentinel value like
ULLONG_MAX), sorting descending should keep it at the bottom rather
than ranking it as the largest value. Add SPACESHIP_NUMBER_NA() to
handle this ordering.

Signed-off-by: Ravi Arnan <[email protected]>
Reading /proc/<pid>/smaps_rollup requires PTRACE_MODE_READ. For
processes belonging to another user, an unprivileged caller gets
EACCES and fopenat() returns NULL. Because LinuxProcess is allocated
with xCalloc(), the fields (m_pss, m_swap, m_psswp, m_epss) retained
their 0 initialization, rendering 0 as if it were a measured value.

Widen the smaps fields to unsigned long long to match Row_printKBytes(),
initialize them to ULLONG_MAX, and set them to ULLONG_MAX on read failure
(including partial reads where ferror() is set) so they render as N/A.
Use SPACESHIP_NUMBER_NA() so that N/A ranks lowest in sort order.

Closes htop-dev#1704

Signed-off-by: Ravi Arnan <[email protected]>
Widen the smaps fields to unsigned long long to match Row_printKBytes(),
initialize them to ULLONG_MAX, and pass ULLONG_MAX as the fallback
value to Metric_instance_u64() so unavailable smaps metrics render as
N/A instead of 0. Use SPACESHIP_NUMBER_NA() so that N/A ranks lowest
in sort order.

Signed-off-by: Ravi Arnan <[email protected]>
@ravi-arnan
ravi-arnan force-pushed the fix-1704-smaps-unknown branch from fcfa7c5 to cfe7fe6 Compare October 19, 2026 08:03
@ravi-arnan

Copy link
Copy Markdown
Contributor Author

Split into three commits per your request:

  1. general: add SPACESHIP_NUMBER_NA macro to sort unavailable metrics lowest (Macros.h)
  2. linux: render N/A instead of 0 for unreadable smaps metrics (linux/*)
  3. pcp: render N/A instead of 0 for unavailable smaps metrics (pcp/*)

Each commit builds independently, and the final tree hash is unchanged.

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 Linux 🐧 Linux related issues PCP PCP related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

M_PSS is bugged and zero

2 participants