Skip to content
This repository was archived by the owner on Sep 8, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion linux/LinuxProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"
Expand All @@ -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;
Expand Down Expand Up @@ -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, },
};

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
9 changes: 8 additions & 1 deletion linux/LinuxProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"
Expand All @@ -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;
Expand Down
83 changes: 82 additions & 1 deletion linux/LinuxProcessList.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ typedef struct LinuxProcessList_ {

CPUData* cpus;
TtyDriver* ttyDrivers;
bool haveSmapsRollup;

#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -486,6 +496,62 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* di
return (errno == 0);
}

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.
Copy link
Copy Markdown
Author

@ntninja ntninja Nov 1, 2018

Choose a reason for hiding this comment

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

@hishamhm: True and well analyzed! However, such reads will never happen for regular smaps because data is returned in chunks of PAGE_SIZE. For smaps_rollup, as you have correctly pointed out, this discussion is irrelevant anyways.

Regarding the verification of this claim: The linked code does appear to write out each result entry separately and when I run strace cat /proc/$$/smaps each read returns as many entries as fit into a 4k-page, without any “half-entries”.

(For the most part we have to trust the previous author's judgment here. Fortunately however we do not need to worry about kernel changes, since we're only really talking about Linux ~2.6.26 up to 2.13 here).


char buffer[PAGE_SIZE];// 4k
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;

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
Comment thread
ntninja marked this conversation as resolved.

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) {
Expand Down Expand Up @@ -815,6 +881,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, this->haveSmapsRollup);
}
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];
Expand Down
1 change: 1 addition & 0 deletions linux/LinuxProcessList.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ typedef struct LinuxProcessList_ {

CPUData* cpus;
TtyDriver* ttyDrivers;
bool haveSmapsRollup;

#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
Expand Down