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 5f697078..6b48780b 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 6ce3037d..6b074ac9 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 5f38540c..a02da072 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 a02da072..c1ccd420 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 f30b487d..59e83bcb 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;