From fc0bf546c3ceb03fedb08253756cae63d15499b5 Mon Sep 17 00:00:00 2001
From: Alexander Schlarb
Date: Tue, 9 Oct 2018 21:49:29 +0200
Subject: [PATCH 1/2] Linux: Add PSS (proportional set size), Swap and SwapPSS
calculation
Original code was written by *Craig M. Brandenburg* for htop 1.0.2
Many performance improvements by GitHub user *linvinus*, ported to htop 2.0.2
---
linux/LinuxProcess.c | 21 ++++++++++++-
linux/LinuxProcess.h | 9 +++++-
linux/LinuxProcessList.c | 67 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c
index 5f697078f..6b48780b3 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -24,6 +24,7 @@ in the source distribution for its full text.
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
+#define PROCESS_FLAG_LINUX_SMAPS 0x2000
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@@ -87,7 +88,10 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
- LAST_PROCESSFIELD = 119,
+ M_PSS = 119,
+ M_SWAP = 120,
+ M_PSSWP = 121,
+ LAST_PROCESSFIELD = 122,
} LinuxProcessField;
#include "IOPriority.h"
@@ -103,6 +107,9 @@ typedef struct LinuxProcess_ {
unsigned long long int cutime;
unsigned long long int cstime;
long m_share;
+ long m_pss;
+ long m_swap;
+ long m_psswp;
long m_trs;
long m_drs;
long m_lrs;
@@ -239,6 +246,9 @@ ProcessFieldData Process_fields[] = {
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, },
#endif
+ [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
+ [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, },
+ [M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
@@ -344,6 +354,9 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
case M_LRS: Process_humanNumber(str, lp->m_lrs * PAGE_SIZE_KB, coloring); return;
case M_TRS: Process_humanNumber(str, lp->m_trs * PAGE_SIZE_KB, coloring); return;
case M_SHARE: Process_humanNumber(str, lp->m_share * PAGE_SIZE_KB, coloring); return;
+ case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return;
+ case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return;
+ case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return;
case UTIME: Process_printTime(str, lp->utime); return;
case STIME: Process_printTime(str, lp->stime); return;
case CUTIME: Process_printTime(str, lp->cutime); return;
@@ -435,6 +448,12 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
return (p2->m_trs - p1->m_trs);
case M_SHARE:
return (p2->m_share - p1->m_share);
+ case M_PSS:
+ return (p2->m_pss - p1->m_pss);
+ case M_SWAP:
+ return (p2->m_swap - p1->m_swap);
+ case M_PSSWP:
+ return (p2->m_psswp - p1->m_psswp);
case UTIME: diff = p2->utime - p1->utime; goto test_diff;
case CUTIME: diff = p2->cutime - p1->cutime; goto test_diff;
case STIME: diff = p2->stime - p1->stime; goto test_diff;
diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h
index 6ce3037d2..6b074ac98 100644
--- a/linux/LinuxProcess.h
+++ b/linux/LinuxProcess.h
@@ -15,6 +15,7 @@ in the source distribution for its full text.
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
+#define PROCESS_FLAG_LINUX_SMAPS 0x2000
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@@ -78,7 +79,10 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
- LAST_PROCESSFIELD = 119,
+ M_PSS = 119,
+ M_SWAP = 120,
+ M_PSSWP = 121,
+ LAST_PROCESSFIELD = 122,
} LinuxProcessField;
#include "IOPriority.h"
@@ -94,6 +98,9 @@ typedef struct LinuxProcess_ {
unsigned long long int cutime;
unsigned long long int cstime;
long m_share;
+ long m_pss;
+ long m_swap;
+ long m_psswp;
long m_trs;
long m_drs;
long m_lrs;
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index 5f38540c6..a02da072c 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -486,6 +486,58 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* di
return (errno == 0);
}
+static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* dirname, const char* name) {
+ //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.
+
+ char buffer[PAGE_SIZE];// 4k
+ char *start,*end;
+ ssize_t nread=0;
+ int tmp=0;
+ snprintf(buffer, MAX_NAME, "%s/%s/smaps", dirname, name);
+ int fd = open(buffer, O_RDONLY);
+ if (fd == -1)
+ return false;
+
+ process->m_pss = 0;
+ process->m_swap = 0;
+ process->m_psswp = 0;
+
+ while ( ( nread = read(fd,buffer, sizeof(buffer)) ) > 0 ){
+ start = (char *)&buffer;
+ end = start + nread;
+ do{//parse 4k block
+
+ if( (tmp = (end - start)) > 0 &&
+ (start = memmem(start,tmp,"\nPss:",5)) != NULL )
+ {
+ process->m_pss += strtol(start+5, &start, 10);
+ start += 3;//now we must be at the end of line "Pss: 0 kB"
+ }else
+ break; //read next 4k block
+
+ if( (tmp = (end - start)) > 0 &&
+ (start = memmem(start,tmp,"\nSwap:",6)) != NULL )
+ {
+ process->m_swap += strtol(start+6, &start, 10);
+ start += 3;
+ }else
+ break;
+
+ if( (tmp = (end - start)) > 0 &&
+ (start = memmem(start,tmp,"\nSwapPss:",9)) != NULL )
+ {
+ process->m_psswp += strtol(start+9, &start, 10);
+ start += 3;
+ }else
+ break;
+
+ }while(1);
+ }//while read
+ close(fd);
+ return true;
+}
+
#ifdef HAVE_OPENVZ
static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* dirname, const char* name) {
@@ -815,6 +867,21 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
if (! LinuxProcessList_readStatmFile(lp, dirname, name))
goto errorReadingProcess;
+ if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)){
+ if (!parent){
+ // Read smaps file of each process only every second pass to improve performance
+ static int smaps_flag = 0;
+ if ((pid & 1) == smaps_flag){
+ LinuxProcessList_readSmapsFile(lp, dirname, name);
+ }
+ if (pid == 1) {
+ smaps_flag = !smaps_flag;
+ }
+ } else {
+ lp->m_pss = ((LinuxProcess*)parent)->m_pss;
+ }
+ }
+
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
char command[MAX_NAME+1];
From 078c2ddde5a64a8a9b3bcf40d62e1f81477db029 Mon Sep 17 00:00:00 2001
From: Alexander Schlarb
Date: Tue, 16 Oct 2018 20:08:23 +0200
Subject: [PATCH 2/2] Linux: Use /proc/*/smaps_rollup for improved PSS parsing
speed
---
linux/LinuxProcessList.c | 20 +++++++++++++++++---
linux/LinuxProcessList.h | 1 +
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index a02da072c..c1ccd4202 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -89,6 +89,7 @@ typedef struct LinuxProcessList_ {
CPUData* cpus;
TtyDriver* ttyDrivers;
+ bool haveSmapsRollup;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
@@ -240,8 +241,17 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
LinuxProcessList_initNetlinkSocket(this);
#endif
+ // Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+)
+ FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r");
+ if(file != NULL) {
+ this->haveSmapsRollup = true;
+ fclose(file);
+ } else {
+ this->haveSmapsRollup = false;
+ }
+
// Update CPU count:
- FILE* file = fopen(PROCSTATFILE, "r");
+ file = fopen(PROCSTATFILE, "r");
if (file == NULL) {
CRT_fatalError("Cannot open " PROCSTATFILE);
}
@@ -486,7 +496,7 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* di
return (errno == 0);
}
-static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* dirname, const char* name) {
+static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* dirname, const char* name, bool haveSmapsRollup) {
//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.
@@ -494,7 +504,11 @@ static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* di
char *start,*end;
ssize_t nread=0;
int tmp=0;
+ if(haveSmapsRollup) {// only available in Linux 4.14+
+ snprintf(buffer, MAX_NAME, "%s/%s/smaps_rollup", dirname, name);
+ } else {
snprintf(buffer, MAX_NAME, "%s/%s/smaps", dirname, name);
+ }
int fd = open(buffer, O_RDONLY);
if (fd == -1)
return false;
@@ -872,7 +886,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
// Read smaps file of each process only every second pass to improve performance
static int smaps_flag = 0;
if ((pid & 1) == smaps_flag){
- LinuxProcessList_readSmapsFile(lp, dirname, name);
+ LinuxProcessList_readSmapsFile(lp, dirname, name, this->haveSmapsRollup);
}
if (pid == 1) {
smaps_flag = !smaps_flag;
diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h
index f30b487d6..59e83bcb1 100644
--- a/linux/LinuxProcessList.h
+++ b/linux/LinuxProcessList.h
@@ -62,6 +62,7 @@ typedef struct LinuxProcessList_ {
CPUData* cpus;
TtyDriver* ttyDrivers;
+ bool haveSmapsRollup;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;