From 2cea6c6f91948ece834cb5fd8ade434b59435da7 Mon Sep 17 00:00:00 2001 From: Ravi Arnan Date: Mon, 19 Oct 2026 15:57:19 +0800 Subject: [PATCH 1/3] general: add SPACESHIP_NUMBER_NA macro to sort unavailable metrics lowest 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 --- Macros.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Macros.h b/Macros.h index 6b064abb69..4d870afe9b 100644 --- a/Macros.h +++ b/Macros.h @@ -33,6 +33,10 @@ in the source distribution for its full text. #define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b))) #endif +#ifndef SPACESHIP_NUMBER_NA +#define SPACESHIP_NUMBER_NA(a, b, na) (((a) == (na)) ? (((b) == (na)) ? 0 : -1) : (((b) == (na)) ? 1 : SPACESHIP_NUMBER(a, b))) +#endif + #ifndef SPACESHIP_NULLSTR #define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "") #endif From 2dab1bacffe416cdc760e5ec320632144d36e7c9 Mon Sep 17 00:00:00 2001 From: Ravi Arnan Date: Mon, 19 Oct 2026 15:57:25 +0800 Subject: [PATCH 2/3] linux: render N/A instead of 0 for unreadable smaps metrics Reading /proc//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 #1704 Signed-off-by: Ravi Arnan --- linux/LinuxProcess.c | 12 ++++++++---- linux/LinuxProcess.h | 8 ++++---- linux/LinuxProcessTable.c | 26 ++++++++++++++++++++++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 930ef1ff92..9b79e68ca3 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -113,6 +113,10 @@ Process* LinuxProcess_new(const Machine* host) { LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess)); Object_setClass(this, Class(LinuxProcess)); Process_init(&this->super, host); + this->m_pss = ULLONG_MAX; + this->m_swap = ULLONG_MAX; + this->m_psswp = ULLONG_MAX; + this->m_epss = ULLONG_MAX; return (Process*)this; } @@ -379,13 +383,13 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce case M_PRIV: return SPACESHIP_NUMBER(p1->m_priv, p2->m_priv); case M_PSS: - return SPACESHIP_NUMBER(p1->m_pss, p2->m_pss); + return SPACESHIP_NUMBER_NA(p1->m_pss, p2->m_pss, ULLONG_MAX); case M_SWAP: - return SPACESHIP_NUMBER(p1->m_swap, p2->m_swap); + return SPACESHIP_NUMBER_NA(p1->m_swap, p2->m_swap, ULLONG_MAX); case M_PSSWP: - return SPACESHIP_NUMBER(p1->m_psswp, p2->m_psswp); + return SPACESHIP_NUMBER_NA(p1->m_psswp, p2->m_psswp, ULLONG_MAX); case M_EPSS: - return SPACESHIP_NUMBER(p1->m_epss, p2->m_epss); + return SPACESHIP_NUMBER_NA(p1->m_epss, p2->m_epss, ULLONG_MAX); case UTIME: return SPACESHIP_NUMBER(p1->utime, p2->utime); case CUTIME: diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 0a25bc70be..c533edfa46 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -41,10 +41,10 @@ typedef struct LinuxProcess_ { unsigned long long int cstime; long m_share; long m_priv; - long m_pss; - long m_swap; - long m_psswp; - long m_epss; + unsigned long long m_pss; + unsigned long long m_swap; + unsigned long long m_psswp; + unsigned long long m_epss; long m_trs; long m_drs; long m_lrs; diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index fc43baac30..95fb17d24e 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -878,8 +878,13 @@ static bool LinuxProcessTable_readSmapsFile(LinuxProcess* process, openat_arg_t //http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719 //kernel will return data in chunks of size PAGE_SIZE or less. FILE* fp = fopenat(procFd, haveSmapsRollup ? "smaps_rollup" : "smaps", "r"); - if (!fp) + if (!fp) { + process->m_pss = ULLONG_MAX; + process->m_swap = ULLONG_MAX; + process->m_psswp = ULLONG_MAX; + process->m_epss = ULLONG_MAX; return false; + } process->m_pss = 0; process->m_swap = 0; @@ -891,6 +896,10 @@ static bool LinuxProcessTable_readSmapsFile(LinuxProcess* process, openat_arg_t if (!strchr(buffer, '\n')) { // Partial line, skip to end of this line if (!skipEndOfLine(fp)) { + process->m_pss = ULLONG_MAX; + process->m_swap = ULLONG_MAX; + process->m_psswp = ULLONG_MAX; + process->m_epss = ULLONG_MAX; fclose(fp); return false; } @@ -898,14 +907,23 @@ static bool LinuxProcessTable_readSmapsFile(LinuxProcess* process, openat_arg_t } if (String_startsWith(buffer, "Pss:")) { - process->m_pss += strtol(buffer + 4, NULL, 10); + process->m_pss += strtoull(buffer + 4, NULL, 10); } else if (String_startsWith(buffer, "Swap:")) { - process->m_swap += strtol(buffer + 5, NULL, 10); + process->m_swap += strtoull(buffer + 5, NULL, 10); } else if (String_startsWith(buffer, "SwapPss:")) { - process->m_psswp += strtol(buffer + 8, NULL, 10); + process->m_psswp += strtoull(buffer + 8, NULL, 10); } } + if (ferror(fp)) { + process->m_pss = ULLONG_MAX; + process->m_swap = ULLONG_MAX; + process->m_psswp = ULLONG_MAX; + process->m_epss = ULLONG_MAX; + fclose(fp); + return false; + } + process->m_epss = process->m_pss + process->m_psswp; fclose(fp); From cfe7fe6ebd14e3b5db3abf1ee0d07f10c237f642 Mon Sep 17 00:00:00 2001 From: Ravi Arnan Date: Mon, 19 Oct 2026 15:57:33 +0800 Subject: [PATCH 3/3] pcp: render N/A instead of 0 for unavailable smaps metrics 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 --- pcp/PCPProcess.c | 10 +++++++--- pcp/PCPProcess.h | 6 +++--- pcp/PCPProcessTable.c | 6 +++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pcp/PCPProcess.c b/pcp/PCPProcess.c index ae4a3e0dcb..ef21700226 100644 --- a/pcp/PCPProcess.c +++ b/pcp/PCPProcess.c @@ -11,6 +11,7 @@ in the source distribution for its full text. #include "pcp/PCPProcess.h" +#include #include #include #include @@ -97,6 +98,9 @@ Process* PCPProcess_new(const Machine* host) { PCPProcess* this = xCalloc(1, sizeof(PCPProcess)); Object_setClass(this, Class(PCPProcess)); Process_init(&this->super, host); + this->m_pss = ULLONG_MAX; + this->m_swap = ULLONG_MAX; + this->m_psswp = ULLONG_MAX; return (Process*)this; } @@ -229,11 +233,11 @@ static int PCPProcess_compareByKey(const Process* v1, const Process* v2, Process case M_PRIV: return SPACESHIP_NUMBER(p1->m_priv, p2->m_priv); case M_PSS: - return SPACESHIP_NUMBER(p1->m_pss, p2->m_pss); + return SPACESHIP_NUMBER_NA(p1->m_pss, p2->m_pss, ULLONG_MAX); case M_SWAP: - return SPACESHIP_NUMBER(p1->m_swap, p2->m_swap); + return SPACESHIP_NUMBER_NA(p1->m_swap, p2->m_swap, ULLONG_MAX); case M_PSSWP: - return SPACESHIP_NUMBER(p1->m_psswp, p2->m_psswp); + return SPACESHIP_NUMBER_NA(p1->m_psswp, p2->m_psswp, ULLONG_MAX); case UTIME: return SPACESHIP_NUMBER(p1->utime, p2->utime); case CUTIME: diff --git a/pcp/PCPProcess.h b/pcp/PCPProcess.h index e95375714f..2baafdd8b0 100644 --- a/pcp/PCPProcess.h +++ b/pcp/PCPProcess.h @@ -37,9 +37,9 @@ typedef struct PCPProcess_ { unsigned long long int cstime; long m_share; long m_priv; - long m_pss; - long m_swap; - long m_psswp; + unsigned long long m_pss; + unsigned long long m_swap; + unsigned long long m_psswp; long m_trs; long m_drs; long m_lrs; diff --git a/pcp/PCPProcessTable.c b/pcp/PCPProcessTable.c index c227dca08c..08432163a7 100644 --- a/pcp/PCPProcessTable.c +++ b/pcp/PCPProcessTable.c @@ -212,9 +212,9 @@ static void PCPProcessTable_updateMemory(PCPProcess* pp, int pid, int offset) { } static void PCPProcessTable_updateSmaps(PCPProcess* pp, pid_t pid, int offset) { - pp->m_pss = Metric_instance_u64(PCP_PROC_SMAPS_PSS, pid, offset, 0); - pp->m_swap = Metric_instance_u64(PCP_PROC_SMAPS_SWAP, pid, offset, 0); - pp->m_psswp = Metric_instance_u64(PCP_PROC_SMAPS_SWAPPSS, pid, offset, 0); + pp->m_pss = Metric_instance_u64(PCP_PROC_SMAPS_PSS, pid, offset, ULLONG_MAX); + pp->m_swap = Metric_instance_u64(PCP_PROC_SMAPS_SWAP, pid, offset, ULLONG_MAX); + pp->m_psswp = Metric_instance_u64(PCP_PROC_SMAPS_SWAPPSS, pid, offset, ULLONG_MAX); } static void PCPProcessTable_readOomData(PCPProcess* pp, int pid, int offset) {