aboutsummaryrefslogtreecommitdiffstats
path: root/linux/LinuxProcessList.c
diff options
context:
space:
mode:
authorDaniel Lange <DLange@git.local>2020-12-07 10:26:01 +0100
committerDaniel Lange <DLange@git.local>2020-12-07 10:26:01 +0100
commit65357c8c46154de4e4eca14075bfe5523bb5fc14 (patch)
tree8f430ee5a0d5de377c4e7c94e47842a27c70d7e8 /linux/LinuxProcessList.c
parentf80394a20254938142011855f2954b3f63fe5909 (diff)
downloaddebian_htop-65357c8c46154de4e4eca14075bfe5523bb5fc14.tar.gz
debian_htop-65357c8c46154de4e4eca14075bfe5523bb5fc14.tar.bz2
debian_htop-65357c8c46154de4e4eca14075bfe5523bb5fc14.zip
New upstream version 3.0.3upstream/3.0.3
Diffstat (limited to 'linux/LinuxProcessList.c')
-rw-r--r--linux/LinuxProcessList.c1564
1 files changed, 1141 insertions, 423 deletions
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index 8dae13c..b9ba247 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -1,95 +1,98 @@
/*
htop - LinuxProcessList.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "LinuxProcessList.h"
-#include "LinuxProcess.h"
-#include "CRT.h"
-#include "StringUtils.h"
-#include <errno.h>
-#include <sys/time.h>
-#include <sys/utsname.h>
-#include <sys/stat.h>
-#include <unistd.h>
+
+#include <assert.h>
#include <dirent.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <time.h>
-#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/types.h>
-#include <fcntl.h>
-#ifdef MAJOR_IN_MKDEV
-#include <sys/mkdev.h>
-#elif defined(MAJOR_IN_SYSMACROS)
-#include <sys/sysmacros.h>
-#endif
#ifdef HAVE_DELAYACCT
+#include <linux/netlink.h>
+#include <linux/taskstats.h>
#include <netlink/attr.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
#include <netlink/netlink.h>
+#include <netlink/socket.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
-#include <netlink/socket.h>
-#include <netlink/msg.h>
-#include <linux/taskstats.h>
#endif
-static ssize_t xread(int fd, void *buf, size_t count) {
- // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
- size_t alreadyRead = 0;
- for(;;) {
- ssize_t res = read(fd, buf, count);
- if (res == -1 && errno == EINTR) continue;
- if (res > 0) {
- buf = ((char*)buf)+res;
- count -= res;
- alreadyRead += res;
- }
- if (res == -1) return -1;
- if (count == 0 || res == 0) return alreadyRead;
- }
+#include "Compat.h"
+#include "CRT.h"
+#include "LinuxProcess.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Process.h"
+#include "Settings.h"
+#include "XUtils.h"
+
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#elif defined(MAJOR_IN_SYSMACROS)
+#include <sys/sysmacros.h>
+#endif
+
+#ifdef HAVE_SENSORS_SENSORS_H
+#include "LibSensors.h"
+#endif
+
+
+static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
+ assert(String_eq(mode, "r")); /* only currently supported mode */
+
+ int fd = Compat_openat(openatArg, pathname, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ FILE* stream = fdopen(fd, mode);
+ if (!stream)
+ close(fd);
+
+ return stream;
}
static int sortTtyDrivers(const void* va, const void* vb) {
- TtyDriver* a = (TtyDriver*) va;
- TtyDriver* b = (TtyDriver*) vb;
- return (a->major == b->major) ? (a->minorFrom - b->minorFrom) : (a->major - b->major);
+ const TtyDriver* a = (const TtyDriver*) va;
+ const TtyDriver* b = (const TtyDriver*) vb;
+
+ int r = SPACESHIP_NUMBER(a->major, b->major);
+ if (r)
+ return r;
+
+ return SPACESHIP_NUMBER(a->minorFrom, b->minorFrom);
}
static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
TtyDriver* ttyDrivers;
- int fd = open(PROCTTYDRIVERSFILE, O_RDONLY);
- if (fd == -1)
- return;
- char* buf = NULL;
- int bufSize = MAX_READ;
- int bufLen = 0;
- for(;;) {
- buf = realloc(buf, bufSize);
- int size = xread(fd, buf + bufLen, MAX_READ);
- if (size <= 0) {
- buf[bufLen] = '\0';
- close(fd);
- break;
- }
- bufLen += size;
- bufSize += MAX_READ;
- }
- if (bufLen == 0) {
- free(buf);
+
+ char buf[16384];
+ ssize_t r = xReadfile(PROCTTYDRIVERSFILE, buf, sizeof(buf));
+ if (r < 0)
return;
- }
+
int numDrivers = 0;
int allocd = 10;
- ttyDrivers = malloc(sizeof(TtyDriver) * allocd);
+ ttyDrivers = xMalloc(sizeof(TtyDriver) * allocd);
char* at = buf;
while (*at != '\0') {
at = strchr(at, ' '); // skip first token
@@ -97,7 +100,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
char* token = at; // mark beginning of path
at = strchr(at, ' '); // find end of path
*at = '\0'; at++; // clear and skip
- ttyDrivers[numDrivers].path = strdup(token); // save
+ ttyDrivers[numDrivers].path = xStrdup(token); // save
while (*at == ' ') at++; // skip spaces
token = at; // mark beginning of major
at = strchr(at, ' '); // find end of major
@@ -123,12 +126,11 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
numDrivers++;
if (numDrivers == allocd) {
allocd += 10;
- ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * allocd);
+ ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * allocd);
}
}
- free(buf);
numDrivers++;
- ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
+ ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
ttyDrivers[numDrivers - 1].path = NULL;
qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers);
this->ttyDrivers = ttyDrivers;
@@ -149,6 +151,46 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
#endif
+static int LinuxProcessList_computeCPUcount(void) {
+ FILE* file = fopen(PROCSTATFILE, "r");
+ if (file == NULL) {
+ CRT_fatalError("Cannot open " PROCSTATFILE);
+ }
+
+ int cpus = 0;
+ char buffer[PROC_LINE_LENGTH + 1];
+ while (fgets(buffer, sizeof(buffer), file)) {
+ if (String_startsWith(buffer, "cpu")) {
+ cpus++;
+ }
+ }
+
+ fclose(file);
+
+ /* subtract raw cpu entry */
+ if (cpus > 0) {
+ cpus--;
+ }
+
+ return cpus;
+}
+
+static void LinuxProcessList_updateCPUcount(LinuxProcessList* this) {
+ ProcessList* pl = &(this->super);
+ int cpus = LinuxProcessList_computeCPUcount();
+ if (cpus == 0 || cpus == pl->cpuCount)
+ return;
+
+ pl->cpuCount = cpus;
+ free(this->cpus);
+ this->cpus = xCalloc(cpus + 1, sizeof(CPUData));
+
+ for (int i = 0; i <= cpus; i++) {
+ this->cpus[i].totalTime = 1;
+ this->cpus[i].totalPeriod = 1;
+ }
+}
+
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
@@ -162,41 +204,47 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
// Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+)
FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r");
- if(file != NULL) {
+ if (file != NULL) {
this->haveSmapsRollup = true;
fclose(file);
} else {
this->haveSmapsRollup = false;
}
- // Update CPU count:
- file = fopen(PROCSTATFILE, "r");
- if (file == NULL) {
- CRT_fatalError("Cannot open " PROCSTATFILE);
- }
- int cpus = 0;
- do {
- char buffer[PROC_LINE_LENGTH + 1];
- if (fgets(buffer, PROC_LINE_LENGTH + 1, file) == NULL) {
- CRT_fatalError("No btime in " PROCSTATFILE);
- } else if (String_startsWith(buffer, "cpu")) {
- cpus++;
- } else if (String_startsWith(buffer, "btime ")) {
- if (sscanf(buffer, "btime %lld\n", &btime) != 1)
- CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
- break;
+ // Read btime
+ {
+ FILE* statfile = fopen(PROCSTATFILE, "r");
+ if (statfile == NULL) {
+ CRT_fatalError("Cannot open " PROCSTATFILE);
}
- } while(true);
- fclose(file);
+ while (true) {
+ char buffer[PROC_LINE_LENGTH + 1];
+ if (fgets(buffer, sizeof(buffer), statfile) == NULL) {
+ CRT_fatalError("No btime in " PROCSTATFILE);
+ } else if (String_startsWith(buffer, "btime ")) {
+ if (sscanf(buffer, "btime %lld\n", &btime) != 1) {
+ CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
+ }
+ break;
+ }
+ }
- pl->cpuCount = MAXIMUM(cpus - 1, 1);
- this->cpus = xCalloc(cpus, sizeof(CPUData));
+ fclose(statfile);
+ }
- for (int i = 0; i < cpus; i++) {
- this->cpus[i].totalTime = 1;
- this->cpus[i].totalPeriod = 1;
+ // Initialize CPU count
+ {
+ int cpus = LinuxProcessList_computeCPUcount();
+ pl->cpuCount = MAXIMUM(cpus, 1);
+ this->cpus = xCalloc(cpus + 1, sizeof(CPUData));
+
+ for (int i = 0; i <= cpus; i++) {
+ this->cpus[i].totalTime = 1;
+ this->cpus[i].totalPeriod = 1;
+ }
}
+
return pl;
}
@@ -205,7 +253,7 @@ void ProcessList_delete(ProcessList* pl) {
ProcessList_done(pl);
free(this->cpus);
if (this->ttyDrivers) {
- for(int i = 0; this->ttyDrivers[i].path; i++) {
+ for (int i = 0; this->ttyDrivers[i].path; i++) {
free(this->ttyDrivers[i].path);
}
free(this->ttyDrivers);
@@ -219,38 +267,41 @@ void ProcessList_delete(ProcessList* pl) {
free(this);
}
-static double jiffy = 0.0;
-
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
- if(jiffy == 0.0) jiffy = sysconf(_SC_CLK_TCK);
- double jiffytime = 1.0 / jiffy;
- return (unsigned long long) t * jiffytime * 100;
+ static long jiffy = -1;
+ if (jiffy == -1) {
+ errno = 0;
+ jiffy = sysconf(_SC_CLK_TCK);
+ if (errno || -1 == jiffy) {
+ jiffy = -1;
+ return t; // Assume 100Hz clock
+ }
+ }
+ return t * 100 / jiffy;
}
-static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command, int* commLen) {
+static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, int* commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1)
- return false;
-
- static char buf[MAX_READ+1];
+ const int commLenIn = *commLen;
+ *commLen = 0;
- int size = xread(fd, buf, MAX_READ);
- close(fd);
- if (size <= 0) return false;
- buf[size] = '\0';
+ char buf[MAX_READ + 1];
+ ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf));
+ if (r < 0)
+ return false;
assert(process->pid == atoi(buf));
- char *location = strchr(buf, ' ');
- if (!location) return false;
+ char* location = strchr(buf, ' ');
+ if (!location)
+ return false;
location += 2;
- char *end = strrchr(location, ')');
- if (!end) return false;
+ char* end = strrchr(location, ')');
+ if (!end)
+ return false;
- int commsize = end - location;
+ int commsize = MINIMUM(end - location, commLenIn - 1);
+ // deepcode ignore BufferOverflow: commsize is bounded by the allocated length passed in by commLen, saved into commLenIn
memcpy(command, location, commsize);
command[commsize] = '\0';
*commLen = commsize;
@@ -292,10 +343,16 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
location += 1;
process->nlwp = strtol(location, &location, 10);
location += 1;
- location = strchr(location, ' ')+1;
- lp->starttime = strtoll(location, &location, 10);
+ location = strchr(location, ' ') + 1;
+ if (process->starttime_ctime == 0) {
+ process->starttime_ctime = btime + LinuxProcess_adjustTime(strtoll(location, &location, 10)) / 100;
+ } else {
+ location = strchr(location, ' ') + 1;
+ }
location += 1;
- for (int i=0; i<15; i++) location = strchr(location, ' ')+1;
+ for (int i = 0; i < 15; i++) {
+ location = strchr(location, ' ') + 1;
+ }
process->exit_signal = strtol(location, &location, 10);
location += 1;
assert(location != NULL);
@@ -307,30 +364,25 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
}
-static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
- char filename[MAX_NAME+1];
- filename[MAX_NAME] = '\0';
-
- xSnprintf(filename, MAX_NAME, "%s/%s", dirname, name);
+static bool LinuxProcessList_statProcessDir(Process* process, openat_arg_t procFd) {
struct stat sstat;
- int statok = stat(filename, &sstat);
+#ifdef HAVE_OPENAT
+ int statok = fstat(procFd, &sstat);
+#else
+ int statok = stat(procFd, &sstat);
+#endif
if (statok == -1)
return false;
process->st_uid = sstat.st_uid;
return true;
}
-#ifdef HAVE_TASKSTATS
-
-static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirname, char* name, unsigned long long now) {
- char filename[MAX_NAME+1];
- filename[MAX_NAME] = '\0';
-
- xSnprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1) {
- process->io_rate_read_bps = -1;
- process->io_rate_write_bps = -1;
+static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long now) {
+ char buffer[1024];
+ ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer));
+ if (r < 0) {
+ process->io_rate_read_bps = NAN;
+ process->io_rate_write_bps = NAN;
process->io_rchar = -1LL;
process->io_wchar = -1LL;
process->io_syscr = -1LL;
@@ -343,171 +395,361 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
return;
}
- char buffer[1024];
- ssize_t buflen = xread(fd, buffer, 1023);
- close(fd);
- if (buflen < 1) return;
- buffer[buflen] = '\0';
unsigned long long last_read = process->io_read_bytes;
unsigned long long last_write = process->io_write_bytes;
- char *buf = buffer;
- char *line = NULL;
+ char* buf = buffer;
+ char* line = NULL;
while ((line = strsep(&buf, "\n")) != NULL) {
switch (line[0]) {
case 'r':
- if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
- process->io_rchar = strtoull(line+7, NULL, 10);
- else if (strncmp(line+1, "ead_bytes: ", 11) == 0) {
- process->io_read_bytes = strtoull(line+12, NULL, 10);
+ if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
+ process->io_rchar = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "ead_bytes: ")) {
+ process->io_read_bytes = strtoull(line + 12, NULL, 10);
process->io_rate_read_bps =
- ((double)(process->io_read_bytes - last_read))/(((double)(now - process->io_rate_read_time))/1000);
+ ((double)(process->io_read_bytes - last_read)) / (((double)(now - process->io_rate_read_time)) / 1000);
process->io_rate_read_time = now;
}
break;
case 'w':
- if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
- process->io_wchar = strtoull(line+7, NULL, 10);
- else if (strncmp(line+1, "rite_bytes: ", 12) == 0) {
- process->io_write_bytes = strtoull(line+13, NULL, 10);
+ if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
+ process->io_wchar = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "rite_bytes: ")) {
+ process->io_write_bytes = strtoull(line + 13, NULL, 10);
process->io_rate_write_bps =
- ((double)(process->io_write_bytes - last_write))/(((double)(now - process->io_rate_write_time))/1000);
+ ((double)(process->io_write_bytes - last_write)) / (((double)(now - process->io_rate_write_time)) / 1000);
process->io_rate_write_time = now;
}
break;
case 's':
- if (line[4] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
- process->io_syscr = strtoull(line+7, NULL, 10);
- } else if (strncmp(line+1, "yscw: ", 6) == 0) {
- process->io_syscw = strtoull(line+7, NULL, 10);
+ if (line[4] == 'r' && String_startsWith(line + 1, "yscr: ")) {
+ process->io_syscr = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "yscw: ")) {
+ process->io_syscw = strtoull(line + 7, NULL, 10);
}
break;
case 'c':
- if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0) {
- process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10);
- }
+ if (String_startsWith(line + 1, "ancelled_write_bytes: ")) {
+ process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10);
+ }
}
}
}
-#endif
+typedef struct LibraryData_ {
+ uint64_t size;
+ bool exec;
+} LibraryData;
+
+static inline uint64_t fast_strtoull_dec(char **str, int maxlen) {
+ register uint64_t result = 0;
+
+ if (!maxlen)
+ --maxlen;
+
+ while (maxlen-- && **str >= '0' && **str <= '9') {
+ result *= 10;
+ result += **str - '0';
+ (*str)++;
+ }
+
+ return result;
+}
+
+static inline uint64_t fast_strtoull_hex(char **str, int maxlen) {
+ register uint64_t result = 0;
+ register int nibble, letter;
+ const long valid_mask = 0x03FF007E;
+
+ if (!maxlen)
+ --maxlen;
+
+ while (maxlen--) {
+ nibble = (unsigned char)**str;
+ if (!(valid_mask & (1 << (nibble & 0x1F))))
+ break;
+ if ((nibble < '0') || (nibble & ~0x20) > 'F')
+ break;
+ letter = (nibble & 0x40) ? 'A' - '9' - 1 : 0;
+ nibble &=~0x20; // to upper
+ nibble ^= 0x10; // switch letters and digits
+ nibble -= letter;
+ nibble &= 0x0f;
+ result <<= 4;
+ result += (uint64_t)nibble;
+ (*str)++;
+ }
+
+ return result;
+}
+
+static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED hkey_t key, void* value, void* data) {
+ if (!data)
+ return;
+
+ if (!value)
+ return;
+
+ LibraryData* v = (LibraryData *)value;
+ uint64_t* d = (uint64_t *)data;
+ if (!v->exec)
+ return;
+
+ *d += v->size;
+}
+
+static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
+ FILE* mapsfile = fopenat(procFd, "maps", "r");
+ if (!mapsfile)
+ return 0;
+ Hashtable* ht = Hashtable_new(64, true);
+ char buffer[1024];
+ while (fgets(buffer, sizeof(buffer), mapsfile)) {
+ uint64_t map_start;
+ uint64_t map_end;
+ char map_perm[5];
+ unsigned int map_devmaj;
+ unsigned int map_devmin;
+ uint64_t map_inode;
+
+ // Short circuit test: Look for a slash
+ if (!strchr(buffer, '/'))
+ continue;
+
+ // Parse format: "%Lx-%Lx %4s %x %2x:%2x %Ld"
+ char *readptr = buffer;
+
+ map_start = fast_strtoull_hex(&readptr, 16);
+ if ('-' != *readptr++)
+ continue;
+
+ map_end = fast_strtoull_hex(&readptr, 16);
+ if (' ' != *readptr++)
+ continue;
+
+ memcpy(map_perm, readptr, 4);
+ map_perm[4] = '\0';
+ readptr += 4;
+ if (' ' != *readptr++)
+ continue;
+
+ while(*readptr > ' ')
+ readptr++; // Skip parsing this hex value
+ if (' ' != *readptr++)
+ continue;
+
+ map_devmaj = fast_strtoull_hex(&readptr, 4);
+ if (':' != *readptr++)
+ continue;
+
+ map_devmin = fast_strtoull_hex(&readptr, 4);
+ if (' ' != *readptr++)
+ continue;
-static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1)
+ //Minor shortcut: Once we know there's no file for this region, we skip
+ if (!map_devmaj && !map_devmin)
+ continue;
+
+ map_inode = fast_strtoull_dec(&readptr, 20);
+ if (!map_inode)
+ continue;
+
+ LibraryData* libdata = Hashtable_get(ht, map_inode);
+ if (!libdata) {
+ libdata = xCalloc(1, sizeof(LibraryData));
+ Hashtable_put(ht, map_inode, libdata);
+ }
+
+ libdata->size += map_end - map_start;
+ libdata->exec |= 'x' == map_perm[2];
+ }
+
+ fclose(mapsfile);
+
+ uint64_t total_size = 0;
+ Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size);
+
+ Hashtable_delete(ht);
+
+ return total_size / CRT_pageSize;
+}
+
+static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) {
+ FILE* statmfile = fopenat(procFd, "statm", "r");
+ if (!statmfile)
return false;
- char buf[PROC_LINE_LENGTH + 1];
- ssize_t rres = xread(fd, buf, PROC_LINE_LENGTH);
- close(fd);
- if (rres < 1) return false;
-
- char *p = buf;
- errno = 0;
- process->super.m_size = strtol(p, &p, 10); if (*p == ' ') p++;
- process->super.m_resident = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_share = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_trs = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_lrs = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_drs = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_dt = strtol(p, &p, 10);
- return (errno == 0);
+
+ long tmp_m_lrs = 0;
+ int r = fscanf(statmfile, "%ld %ld %ld %ld %ld %ld %ld",
+ &process->super.m_virt,
+ &process->super.m_resident,
+ &process->m_share,
+ &process->m_trs,
+ &tmp_m_lrs,
+ &process->m_drs,
+ &process->m_dt);
+ fclose(statmfile);
+
+ if (r == 7) {
+ if (tmp_m_lrs) {
+ process->m_lrs = tmp_m_lrs;
+ } else if (performLookup) {
+ // Check if we really should recalculate the M_LRS value for this process
+ uint64_t passedTimeInMs = now - process->last_mlrs_calctime;
+
+ uint64_t recheck = ((uint64_t)rand()) % 2048;
+
+ if(passedTimeInMs > 2000 || passedTimeInMs > recheck) {
+ process->last_mlrs_calctime = now;
+ process->m_lrs = LinuxProcessList_calcLibSize(procFd);
+ }
+ } else {
+ // Keep previous value
+ }
+ }
+
+ return r == 7;
}
-static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* dirname, const char* name, bool haveSmapsRollup) {
+static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, 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.
-
- 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, PAGE_SIZE-1, "%s/%s/smaps_rollup", dirname, name);
- } else {
- snprintf(buffer, PAGE_SIZE-1, "%s/%s/smaps", dirname, name);
- }
- int fd = open(buffer, O_RDONLY);
- if (fd == -1)
+ FILE* f = fopenat(procFd, haveSmapsRollup ? "smaps_rollup" : "smaps", "r");
+ if (!f)
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);
+ char buffer[256];
+ while (fgets(buffer, sizeof(buffer), f)) {
+ if (!strchr(buffer, '\n')) {
+ // Partial line, skip to end of this line
+ while (fgets(buffer, sizeof(buffer), f)) {
+ if (strchr(buffer, '\n')) {
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (String_startsWith(buffer, "Pss:")) {
+ process->m_pss += strtol(buffer + 4, NULL, 10);
+ } else if (String_startsWith(buffer, "Swap:")) {
+ process->m_swap += strtol(buffer + 5, NULL, 10);
+ } else if (String_startsWith(buffer, "SwapPss:")) {
+ process->m_psswp += strtol(buffer + 8, NULL, 10);
+ }
+ }
+
+ fclose(f);
return true;
}
#ifdef HAVE_OPENVZ
-static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* dirname, const char* name) {
- if ( (access("/proc/vz", R_OK) != 0)) {
+static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t procFd) {
+ if ( (access(PROCDIR "/vz", R_OK) != 0)) {
+ free(process->ctid);
+ process->ctid = NULL;
process->vpid = process->super.pid;
- process->ctid = 0;
return;
}
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
- FILE* file = fopen(filename, "r");
- if (!file)
+
+ FILE* file = fopenat(procFd, "status", "r");
+ if (!file) {
+ free(process->ctid);
+ process->ctid = NULL;
+ process->vpid = process->super.pid;
return;
- (void)! fscanf(file,
- "%*32u %*32s %*1c %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %32u %32u",
- &process->vpid, &process->ctid);
+ }
+
+ bool foundEnvID = false;
+ bool foundVPid = false;
+ char linebuf[256];
+ while (fgets(linebuf, sizeof(linebuf), file) != NULL) {
+ if (strchr(linebuf, '\n') == NULL) {
+ // Partial line, skip to end of this line
+ while (fgets(linebuf, sizeof(linebuf), file) != NULL) {
+ if (strchr(linebuf, '\n') != NULL) {
+ break;
+ }
+ }
+ continue;
+ }
+
+ char* name_value_sep = strchr(linebuf, ':');
+ if (name_value_sep == NULL) {
+ continue;
+ }
+
+ int field;
+ if (0 == strncasecmp(linebuf, "envID", name_value_sep - linebuf)) {
+ field = 1;
+ } else if (0 == strncasecmp(linebuf, "VPid", name_value_sep - linebuf)) {
+ field = 2;
+ } else {
+ continue;
+ }
+
+ do {
+ name_value_sep++;
+ } while (*name_value_sep != '\0' && *name_value_sep <= 32);
+
+ char* value_end = name_value_sep;
+
+ while(*value_end > 32) {
+ value_end++;
+ }
+
+ if (name_value_sep == value_end) {
+ continue;
+ }
+
+ *value_end = '\0';
+
+ switch(field) {
+ case 1:
+ foundEnvID = true;
+ if (!String_eq(name_value_sep, process->ctid ? process->ctid : "")) {
+ free(process->ctid);
+ process->ctid = xStrdup(name_value_sep);
+ }
+ break;
+ case 2:
+ foundVPid = true;
+ process->vpid = strtoul(name_value_sep, NULL, 0);
+ break;
+ default:
+ //Sanity Check: Should never reach here, or the implementation is missing something!
+ assert(false && "OpenVZ handling: Unimplemented case for field handling reached.");
+ }
+ }
+
fclose(file);
- return;
+
+ if (!foundEnvID) {
+ free(process->ctid);
+ process->ctid = NULL;
+ }
+
+ if (!foundVPid) {
+ process->vpid = process->super.pid;
+ }
}
#endif
-#ifdef HAVE_CGROUP
-
-static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
- FILE* file = fopen(filename, "r");
+static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "cgroup", "r");
if (!file) {
- process->cgroup = xStrdup("");
+ if (process->cgroup) {
+ free(process->cgroup);
+ process->cgroup = NULL;
+ }
return;
}
char output[PROC_LINE_LENGTH + 1];
@@ -516,10 +758,14 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* d
int left = PROC_LINE_LENGTH;
while (!feof(file) && left > 0) {
char buffer[PROC_LINE_LENGTH + 1];
- char *ok = fgets(buffer, PROC_LINE_LENGTH, file);
- if (!ok) break;
+ char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
+ if (!ok)
+ break;
+
char* group = strchr(buffer, ':');
- if (!group) break;
+ if (!group)
+ break;
+
if (at != output) {
*at = ';';
at++;
@@ -533,16 +779,13 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* d
process->cgroup = xStrdup(output);
}
-#endif
-
#ifdef HAVE_VSERVER
-static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
- FILE* file = fopen(filename, "r");
+static void LinuxProcessList_readVServerData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "status", "r");
if (!file)
return;
+
char buffer[PROC_LINE_LENGTH + 1];
process->vxid = 0;
while (fgets(buffer, PROC_LINE_LENGTH, file)) {
@@ -568,13 +811,11 @@ static void LinuxProcessList_readVServerData(LinuxProcess* process, const char*
#endif
-static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
- FILE* file = fopen(filename, "r");
- if (!file) {
+static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "oom_score", "r");
+ if (!file)
return;
- }
+
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, PROC_LINE_LENGTH, file)) {
unsigned int oom;
@@ -586,15 +827,94 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirn
fclose(file);
}
+static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "status", "r");
+ if (!file)
+ return;
+
+ char buffer[PROC_LINE_LENGTH + 1];
+ unsigned long ctxt = 0;
+ while (fgets(buffer, PROC_LINE_LENGTH, file)) {
+ if (String_startsWith(buffer, "voluntary_ctxt_switches:")) {
+ unsigned long vctxt;
+ int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt);
+ if (ok >= 1) {
+ ctxt += vctxt;
+ }
+ } else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) {
+ unsigned long nvctxt;
+ int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt);
+ if (ok >= 1) {
+ ctxt += nvctxt;
+ }
+ }
+ }
+ fclose(file);
+ process->ctxt_diff = (ctxt > process->ctxt_total) ? (ctxt - process->ctxt_total) : 0;
+ process->ctxt_total = ctxt;
+}
+
+static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "attr/current", "r");
+ if (!file) {
+ free(process->secattr);
+ process->secattr = NULL;
+ return;
+ }
+
+ char buffer[PROC_LINE_LENGTH + 1];
+ char* res = fgets(buffer, sizeof(buffer), file);
+ fclose(file);
+ if (!res) {
+ free(process->secattr);
+ process->secattr = NULL;
+ return;
+ }
+ char* newline = strchr(buffer, '\n');
+ if (newline) {
+ *newline = '\0';
+ }
+ if (process->secattr && String_eq(process->secattr, buffer)) {
+ return;
+ }
+ free(process->secattr);
+ process->secattr = xStrdup(buffer);
+}
+
+static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) {
+ char pathBuffer[PATH_MAX + 1] = {0};
+
+#if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT)
+ ssize_t r = readlinkat(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1);
+#else
+ char filename[MAX_NAME + 1];
+ xSnprintf(filename, sizeof(filename), "%s/cwd", procFd);
+ ssize_t r = readlink(filename, pathBuffer, sizeof(pathBuffer) - 1);
+#endif
+
+ if (r < 0) {
+ free(process->cwd);
+ process->cwd = NULL;
+ return;
+ }
+
+ pathBuffer[r] = '\0';
+
+ if (process->cwd && String_eq(process->cwd, pathBuffer))
+ return;
+
+ free(process->cwd);
+ process->cwd = xStrdup(pathBuffer);
+}
+
#ifdef HAVE_DELAYACCT
-static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
- struct nlmsghdr *nlhdr;
- struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
- struct nlattr *nlattr;
- struct taskstats *stats;
+static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
+ struct nlmsghdr* nlhdr;
+ struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
+ struct nlattr* nlattr;
+ struct taskstats stats;
int rem;
- unsigned long long int timeDelta;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;
nlhdr = nlmsg_hdr(nlmsg);
@@ -604,26 +924,28 @@ static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
}
if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
- stats = nla_data(nla_next(nla_data(nlattr), &rem));
- assert(lp->super.pid == stats->ac_pid);
- timeDelta = (stats->ac_etime*1000 - lp->delay_read_time);
- #define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x;
- #define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100);
- lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total);
- lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total);
- lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total);
+ memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats));
+ assert(lp->super.pid == (pid_t)stats.ac_pid);
+
+ unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
+ #define BOUNDS(x) (isnan(x) ? 0.0 : ((x) > 100) ? 100.0 : (x))
+ #define DELTAPERC(x,y) BOUNDS((float) ((x) - (y)) / timeDelta * 100)
+ lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total);
+ lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total);
+ lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC
#undef BOUNDS
- lp->swapin_delay_total = stats->swapin_delay_total;
- lp->blkio_delay_total = stats->blkio_delay_total;
- lp->cpu_delay_total = stats->cpu_delay_total;
- lp->delay_read_time = stats->ac_etime*1000;
+
+ lp->swapin_delay_total = stats.swapin_delay_total;
+ lp->blkio_delay_total = stats.blkio_delay_total;
+ lp->cpu_delay_total = stats.cpu_delay_total;
+ lp->delay_read_time = stats.ac_etime * 1000;
}
return NL_OK;
}
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
- struct nl_msg *msg;
+ struct nl_msg* msg;
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
return;
@@ -642,9 +964,9 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
}
if (nl_send_sync(this->netlink_socket, msg) < 0) {
- process->swapin_delay_percent = -1LL;
- process->blkio_delay_percent = -1LL;
- process->cpu_delay_percent = -1LL;
+ process->swapin_delay_percent = NAN;
+ process->blkio_delay_percent = NAN;
+ process->cpu_delay_percent = NAN;
return;
}
@@ -665,18 +987,12 @@ static void setCommand(Process* process, const char* command, int len) {
process->commLen = len;
}
-static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1)
+static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) {
+ char command[4096 + 1]; // max cmdline length on Linux
+ ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command));
+ if (amtRead < 0)
return false;
- char command[4096+1]; // max cmdline length on Linux
- int amtRead = xread(fd, command, sizeof(command) - 1);
- close(fd);
- int tokenEnd = 0;
- int lastChar = 0;
if (amtRead == 0) {
if (process->state == 'Z') {
process->basenameOffset = 0;
@@ -684,25 +1000,183 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
((LinuxProcess*)process)->isKernelThread = true;
}
return true;
- } else if (amtRead < 0) {
- return false;
}
+
+ int tokenEnd = 0;
+ int tokenStart = 0;
+ int lastChar = 0;
+ bool argSepNUL = false;
+ bool argSepSpace = false;
+
for (int i = 0; i < amtRead; i++) {
- if (command[i] == '\0' || command[i] == '\n') {
+ /* newline used as delimiter - when forming the mergedCommand, newline is
+ * converted to space by LinuxProcess_makeCommandStr */
+ if (command[i] == '\0') {
+ command[i] = '\n';
+ } else {
+ /* Record some information for the argument parsing heuristic below. */
+ if (tokenEnd)
+ argSepNUL = true;
+ if (command[i] <= ' ')
+ argSepSpace = true;
+ }
+
+ if (command[i] == '\n') {
if (tokenEnd == 0) {
tokenEnd = i;
}
- command[i] = ' ';
} else {
+ /* htop considers the next character after the last / that is before
+ * basenameOffset, as the start of the basename in cmdline - see
+ * Process_writeCommand */
+ if (!tokenEnd && command[i] == '/') {
+ tokenStart = i + 1;
+ }
lastChar = i;
}
}
+
+ command[lastChar + 1] = '\0';
+
+ if (!argSepNUL && argSepSpace) {
+ /* Argument parsing heuristic.
+ *
+ * This heuristic is used for processes that rewrite their command line.
+ * Normally the command line is split by using NUL bytes between each argument.
+ * But some programs like chrome flatten this using spaces.
+ *
+ * This heuristic tries its best to undo this loss of information.
+ * To achieve this, we treat every character <= 32 as argument separators
+ * (i.e. all of ASCII control sequences and space).
+ * We then search for the basename of the cmdline in the first argument we found that way.
+ * As path names may contain we try to cross-validate if the path we got that way exists.
+ */
+
+ tokenStart = tokenEnd = 0;
+
+ // From initial scan we know there's at least one space.
+ // Check if that's part of a filename for an existing file.
+ if (Compat_faccessat(AT_FDCWD, command, F_OK, AT_SYMLINK_NOFOLLOW) != 0) {
+ // If we reach here the path does not exist.
+ // Thus begin searching for the part of it that actually is.
+
+ int tokenArg0Start = 0;
+
+ for (int i = 0; i <= lastChar; i++) {
+ /* Any ASCII control or space used as delimiter */
+ char tmpCommandChar = command[i];
+
+ if (command[i] <= ' ') {
+ if (!tokenEnd) {
+ command[i] = '\0';
+
+ bool found = Compat_faccessat(AT_FDCWD, command, F_OK, AT_SYMLINK_NOFOLLOW) == 0;
+
+ // Restore if this wasn't it
+ command[i] = found ? '\n' : tmpCommandChar;
+
+ if (found)
+ tokenEnd = i;
+ if (!tokenArg0Start)
+ tokenArg0Start = tokenStart;
+ } else {
+ // Split on every further separator, regardless of path correctness
+ command[i] = '\n';
+ }
+ } else if (!tokenEnd) {
+ if (command[i] == '/' || (command[i] == '\\' && (!tokenStart || command[tokenStart - 1] == '\\'))) {
+ tokenStart = i + 1;
+ } else if (command[i] == ':' && (command[i + 1] != '/' && command[i + 1] != '\\')) {
+ tokenEnd = i;
+ }
+ }
+ }
+
+ if (!tokenEnd) {
+ tokenStart = tokenArg0Start;
+
+ // No token delimiter found, forcibly split
+ for (int i = 0; i <= lastChar; i++) {
+ if (command[i] <= ' ') {
+ command[i] = '\n';
+ if (!tokenEnd) {
+ tokenEnd = i;
+ }
+ }
+ }
+ }
+ }
+ }
+
if (tokenEnd == 0) {
- tokenEnd = amtRead;
+ tokenEnd = lastChar + 1;
+ }
+
+ LinuxProcess *lp = (LinuxProcess *)process;
+ lp->mergedCommand.maxLen = lastChar + 1; /* accommodate cmdline */
+ if (!process->comm || !String_eq(command, process->comm)) {
+ process->basenameOffset = tokenEnd;
+ setCommand(process, command, lastChar + 1);
+ lp->procCmdlineBasenameOffset = tokenStart;
+ lp->procCmdlineBasenameEnd = tokenEnd;
+ lp->mergedCommand.cmdlineChanged = true;
+ }
+
+ /* /proc/[pid]/comm could change, so should be updated */
+ if ((amtRead = xReadfileat(procFd, "comm", command, sizeof(command))) > 0) {
+ command[amtRead - 1] = '\0';
+ lp->mergedCommand.maxLen += amtRead - 1; /* accommodate comm */
+ if (!lp->procComm || !String_eq(command, lp->procComm)) {
+ free(lp->procComm);
+ lp->procComm = xStrdup(command);
+ lp->mergedCommand.commChanged = true;
+ }
+ } else if (lp->procComm) {
+ free(lp->procComm);
+ lp->procComm = NULL;
+ lp->mergedCommand.commChanged = true;
+ }
+
+ char filename[MAX_NAME + 1];
+
+ /* execve could change /proc/[pid]/exe, so procExe should be updated */
+#if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT)
+ amtRead = readlinkat(procFd, "exe", filename, sizeof(filename) - 1);
+#else
+ char path[4096];
+ xSnprintf(path, sizeof(path), "%s/exe", procFd);
+ amtRead = readlink(path, filename, sizeof(filename) - 1);
+#endif
+ if (amtRead > 0) {
+ filename[amtRead] = 0;
+ lp->mergedCommand.maxLen += amtRead; /* accommodate exe */
+ if (!lp->procExe || !String_eq(filename, lp->procExe)) {
+ free(lp->procExe);
+ lp->procExe = xStrdup(filename);
+ lp->procExeLen = amtRead;
+ /* exe is guaranteed to contain at least one /, but validate anyway */
+ while (amtRead && filename[--amtRead] != '/')
+ ;
+ lp->procExeBasenameOffset = amtRead + 1;
+ lp->mergedCommand.exeChanged = true;
+
+ const char* deletedMarker = " (deleted)";
+ if (strlen(lp->procExe) > strlen(deletedMarker)) {
+ lp->procExeDeleted = String_eq(lp->procExe + strlen(lp->procExe) - strlen(deletedMarker), deletedMarker);
+
+ if (lp->procExeDeleted && strlen(lp->procExe) - strlen(deletedMarker) == 1 && lp->procExe[0] == '/') {
+ lp->procExeBasenameOffset = 0;
+ }
+ }
+ }
+ } else if (lp->procExe) {
+ free(lp->procExe);
+ lp->procExe = NULL;
+ lp->procExeLen = 0;
+ lp->procExeBasenameOffset = 0;
+ lp->procExeDeleted = false;
+ lp->mergedCommand.exeChanged = true;
}
- command[lastChar + 1] = '\0';
- process->basenameOffset = tokenEnd;
- setCommand(process, command, lastChar + 1);
return true;
}
@@ -729,47 +1203,71 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned in
unsigned int idx = min - ttyDrivers[i].minorFrom;
struct stat sstat;
char* fullPath;
- for(;;) {
+ for (;;) {
xAsprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx);
int err = stat(fullPath, &sstat);
- if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
+ if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) {
+ return fullPath;
+ }
free(fullPath);
+
xAsprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx);
err = stat(fullPath, &sstat);
- if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
+ if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) {
+ return fullPath;
+ }
free(fullPath);
- if (idx == min) break;
+
+ if (idx == min) {
+ break;
+ }
+
idx = min;
}
int err = stat(ttyDrivers[i].path, &sstat);
- if (err == 0 && tty_nr == sstat.st_rdev) return strdup(ttyDrivers[i].path);
+ if (err == 0 && tty_nr == sstat.st_rdev) {
+ return xStrdup(ttyDrivers[i].path);
+ }
}
char* out;
xAsprintf(&out, "/dev/%u:%u", maj, min);
return out;
}
-static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
+static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period, unsigned long long now) {
ProcessList* pl = (ProcessList*) this;
- DIR* dir;
- struct dirent* entry;
- Settings* settings = pl->settings;
+ const struct dirent* entry;
+ const Settings* settings = pl->settings;
- #ifdef HAVE_TASKSTATS
- unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL;
- #endif
+#ifdef HAVE_OPENAT
+ int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ if (dirFd < 0)
+ return false;
+ DIR* dir = fdopendir(dirFd);
+#else
+ char dirFd[4096];
+ xSnprintf(dirFd, sizeof(dirFd), "%s/%s", parentFd, dirname);
+ DIR* dir = opendir(dirFd);
+#endif
+ if (!dir) {
+ Compat_openatArgClose(dirFd);
+ return false;
+ }
- dir = opendir(dirname);
- if (!dir) return false;
int cpus = pl->cpuCount;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
while ((entry = readdir(dir)) != NULL) {
- char* name = entry->d_name;
+ const char* name = entry->d_name;
+
+ // Ignore all non-directories
+ if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) {
+ continue;
+ }
// The RedHat kernel hides threads with a dot.
// I believe this is non-standard.
- if ((!settings->hideThreads) && name[0] == '.') {
+ if (name[0] == '.') {
name++;
}
@@ -781,107 +1279,160 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
// filename is a number: process directory
int pid = atoi(name);
- if (parent && pid == parent->pid)
+ if (pid <= 0)
continue;
- if (pid <= 0)
+ if (parent && pid == parent->pid)
continue;
- bool preExisting = false;
- Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_New) LinuxProcess_new);
+ bool preExisting;
+ Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
+ LinuxProcess* lp = (LinuxProcess*) proc;
+
proc->tgid = parent ? parent->pid : pid;
- LinuxProcess* lp = (LinuxProcess*) proc;
+#ifdef HAVE_OPENAT
+ int procFd = openat(dirFd, entry->d_name, O_PATH | O_DIRECTORY | O_NOFOLLOW);
+ if (procFd < 0)
+ goto errorReadingProcess;
+#else
+ char procFd[4096];
+ xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
+#endif
- char subdirname[MAX_NAME+1];
- xSnprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
- LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
+ LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period, now);
+
+ /*
+ * These conditions will not trigger on first occurrence, cause we need to
+ * add the process to the ProcessList and do all one time scans
+ * (e.g. parsing the cmdline to detect a kernel thread)
+ * But it will short-circuit subsequent scans.
+ */
+ if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) {
+ proc->updated = true;
+ proc->show = false;
+ pl->kernelThreads++;
+ pl->totalTasks++;
+ Compat_openatArgClose(procFd);
+ continue;
+ }
+ if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
+ proc->updated = true;
+ proc->show = false;
+ pl->userlandThreads++;
+ pl->totalTasks++;
+ Compat_openatArgClose(procFd);
+ continue;
+ }
- #ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO)
- LinuxProcessList_readIoFile(lp, dirname, name, now);
- #endif
+ LinuxProcessList_readIoFile(lp, procFd, now);
- if (! LinuxProcessList_readStatmFile(lp, dirname, name))
+ if (!LinuxProcessList_readStatmFile(lp, procFd, !!(settings->flags & PROCESS_FLAG_LINUX_LRS_FIX), now))
goto errorReadingProcess;
- if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)){
- if (!parent){
+ 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) {
+ LinuxProcessList_readSmapsFile(lp, procFd, this->haveSmapsRollup);
}
if (pid == 1) {
smaps_flag = !smaps_flag;
}
- } else {
- lp->m_pss = ((LinuxProcess*)parent)->m_pss;
- }
+ } else {
+ lp->m_pss = ((const LinuxProcess*)parent)->m_pss;
+ }
}
- proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
-
- char command[MAX_NAME+1];
+ char command[MAX_NAME + 1];
unsigned long long int lasttimes = (lp->utime + lp->stime);
- int commLen = 0;
+ int commLen = sizeof(command);
unsigned int tty_nr = proc->tty_nr;
- if (! LinuxProcessList_readStatFile(proc, dirname, name, command, &commLen))
+ if (! LinuxProcessList_readStatFile(proc, procFd, command, &commLen))
goto errorReadingProcess;
+
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
- if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
+
+ if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO) {
LinuxProcess_updateIOPriority(lp);
- float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
- proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
- if (isnan(proc->percent_cpu)) proc->percent_cpu = 0.0;
- proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(pl->totalMem) * 100.0;
+ }
+
+ /* period might be 0 after system sleep */
+ float percent_cpu = (period < 1e-6) ? 0.0f : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
+ proc->percent_cpu = CLAMP(percent_cpu, 0.0f, cpus * 100.0f);
+ proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(pl->totalMem) * 100.0;
- if(!preExisting) {
+ if (!preExisting) {
- if (! LinuxProcessList_statProcessDir(proc, dirname, name))
+ if (! LinuxProcessList_statProcessDir(proc, procFd))
goto errorReadingProcess;
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
- LinuxProcessList_readOpenVZData(lp, dirname, name);
+ LinuxProcessList_readOpenVZData(lp, procFd);
}
#endif
#ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
- LinuxProcessList_readVServerData(lp, dirname, name);
+ LinuxProcessList_readVServerData(lp, procFd);
}
#endif
- if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) {
+ if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
goto errorReadingProcess;
}
+ Process_fillStarttimeBuffer(proc);
+
ProcessList_add(pl, proc);
} else {
if (settings->updateProcessNames && proc->state != 'Z') {
- if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) {
+ if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
goto errorReadingProcess;
}
}
}
+ /* (Re)Generate the Command string, but only if the process is:
+ * - not a kernel thread, and
+ * - not a zombie or it became zombie under htop's watch, and
+ * - not a user thread or if showThreadNames is not set */
+ if (!Process_isKernelThread(proc) &&
+ (proc->state != 'Z' || lp->mergedCommand.str) &&
+ (!Process_isUserlandThread(proc) || !settings->showThreadNames)) {
+ LinuxProcess_makeCommandStr(proc);
+ }
#ifdef HAVE_DELAYACCT
LinuxProcessList_readDelayAcctData(this, lp);
#endif
- #ifdef HAVE_CGROUP
- if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
- LinuxProcessList_readCGroupFile(lp, dirname, name);
- #endif
+ if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) {
+ LinuxProcessList_readCGroupFile(lp, procFd);
+ }
+
+ if (settings->flags & PROCESS_FLAG_LINUX_OOM) {
+ LinuxProcessList_readOomData(lp, procFd);
+ }
- if (settings->flags & PROCESS_FLAG_LINUX_OOM)
- LinuxProcessList_readOomData(lp, dirname, name);
+ if (settings->flags & PROCESS_FLAG_LINUX_CTXT) {
+ LinuxProcessList_readCtxtData(lp, procFd);
+ }
+
+ if (settings->flags & PROCESS_FLAG_LINUX_SECATTR) {
+ LinuxProcessList_readSecattrData(lp, procFd);
+ }
+
+ if (settings->flags & PROCESS_FLAG_LINUX_CWD) {
+ LinuxProcessList_readCwd(lp, procFd);
+ }
if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
@@ -891,8 +1442,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
proc->basenameOffset = -1;
setCommand(proc, command, commLen);
} else if (settings->showThreadNames) {
- if (! LinuxProcessList_readCmdlineFile(proc, dirname, name))
+ if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
goto errorReadingProcess;
+ }
}
if (Process_isKernelThread(proc)) {
pl->kernelThreads++;
@@ -901,14 +1453,25 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
}
}
+ /* Set at the end when we know if a new entry is a thread */
+ proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
+
pl->totalTasks++;
if (proc->state == 'R')
pl->runningTasks++;
proc->updated = true;
+ Compat_openatArgClose(procFd);
continue;
// Exception handler.
- errorReadingProcess: {
+
+errorReadingProcess:
+ {
+#ifdef HAVE_OPENAT
+ if (procFd >= 0)
+ close(procFd);
+#endif
+
if (preExisting) {
ProcessList_remove(pl, proc);
} else {
@@ -921,6 +1484,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
}
static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
+ unsigned long long int freeMem = 0;
unsigned long long int swapFree = 0;
unsigned long long int shmem = 0;
unsigned long long int sreclaimable = 0;
@@ -932,12 +1496,16 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
char buffer[128];
while (fgets(buffer, 128, file)) {
- #define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %32llu kB", variable)) { break; } } while(0)
+ #define tryRead(label, variable) \
+ if (String_startsWith(buffer, label)) { \
+ sscanf(buffer + strlen(label), " %32llu kB", variable); \
+ break; \
+ }
+
switch (buffer[0]) {
case 'M':
tryRead("MemTotal:", &this->totalMem);
- tryRead("MemFree:", &this->freeMem);
- tryRead("MemShared:", &this->sharedMem);
+ tryRead("MemFree:", &freeMem);
break;
case 'B':
tryRead("Buffers:", &this->buffersMem);
@@ -963,12 +1531,60 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
#undef tryRead
}
- this->usedMem = this->totalMem - this->freeMem;
+ this->usedMem = this->totalMem - freeMem;
this->cachedMem = this->cachedMem + sreclaimable - shmem;
this->usedSwap = this->totalSwap - swapFree;
fclose(file);
}
+static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) {
+ unsigned long long int totalZram = 0;
+ unsigned long long int usedZramComp = 0;
+ unsigned long long int usedZramOrig = 0;
+
+ char mm_stat[34];
+ char disksize[34];
+
+ unsigned int i = 0;
+ for (;;) {
+ xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i);
+ xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i);
+ i++;
+ FILE* disksize_file = fopen(disksize, "r");
+ FILE* mm_stat_file = fopen(mm_stat, "r");
+ if (disksize_file == NULL || mm_stat_file == NULL) {
+ if (disksize_file) {
+ fclose(disksize_file);
+ }
+ if (mm_stat_file) {
+ fclose(mm_stat_file);
+ }
+ break;
+ }
+ unsigned long long int size = 0;
+ unsigned long long int orig_data_size = 0;
+ unsigned long long int compr_data_size = 0;
+
+ if (!fscanf(disksize_file, "%llu\n", &size) ||
+ !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) {
+ fclose(disksize_file);
+ fclose(mm_stat_file);
+ break;
+ }
+
+ totalZram += size;
+ usedZramComp += compr_data_size;
+ usedZramOrig += orig_data_size;
+
+ fclose(disksize_file);
+ fclose(mm_stat_file);
+ }
+
+ this->zram.totalZram = totalZram / 1024;
+ this->zram.usedZramComp = usedZramComp / 1024;
+ this->zram.usedZramOrig = usedZramOrig / 1024;
+}
+
static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
unsigned long long int dbufSize = 0;
unsigned long long int dnodeSize = 0;
@@ -981,8 +1597,17 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
}
char buffer[128];
while (fgets(buffer, 128, file)) {
- #define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { break; } } while(0)
- #define tryReadFlag(label, variable, flag) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { flag = 1; break; } else { flag = 0; } } while(0)
+ #define tryRead(label, variable) \
+ if (String_startsWith(buffer, label)) { \
+ sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
+ break; \
+ }
+ #define tryReadFlag(label, variable, flag) \
+ if (String_startsWith(buffer, label)) { \
+ (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
+ break; \
+ }
+
switch (buffer[0]) {
case 'c':
tryRead("c_max", &lpl->zfs.max);
@@ -1048,10 +1673,13 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
// 5, 7, 8 or 9 of these fields will be set.
// The rest will remain at zero.
char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
- if (!ok) buffer[0] = '\0';
- if (i == 0)
+ if (!ok) {
+ buffer[0] = '\0';
+ }
+
+ if (i == 0) {
(void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
- else {
+ } else {
int cpuid;
(void) sscanf(buffer, "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
assert(cpuid == i - 1);
@@ -1069,7 +1697,7 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
// Since we do a subtraction (usertime - guest) and cputime64_to_clock_t()
// used in /proc/stat rounds down numbers, it can lead to a case where the
// integer overflow.
- #define WRAP_SUBTRACT(a,b) (a > b) ? a - b : 0
+ #define WRAP_SUBTRACT(a,b) (((a) > (b)) ? (a) - (b) : 0)
cpuData->userPeriod = WRAP_SUBTRACT(usertime, cpuData->userTime);
cpuData->nicePeriod = WRAP_SUBTRACT(nicetime, cpuData->niceTime);
cpuData->systemPeriod = WRAP_SUBTRACT(systemtime, cpuData->systemTime);
@@ -1095,88 +1723,178 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
cpuData->stealTime = steal;
cpuData->guestTime = virtalltime;
cpuData->totalTime = totaltime;
-
}
+
double period = (double)this->cpus[0].totalPeriod / cpus;
fclose(file);
return period;
}
-static inline double LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
- ProcessList* pl = (ProcessList*) this;
- Settings* settings = pl->settings;
-
+static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) {
int cpus = this->super.cpuCount;
- assert(cpus > 0);
+ int numCPUsWithFrequency = 0;
+ unsigned long totalFrequency = 0;
+
+ for (int i = 0; i < cpus; ++i) {
+ char pathBuffer[64];
+ xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
+
+ FILE* file = fopen(pathBuffer, "r");
+ if (!file)
+ return -errno;
+
+ unsigned long frequency;
+ if (fscanf(file, "%lu", &frequency) == 1) {
+ /* convert kHz to MHz */
+ frequency = frequency / 1000;
+ this->cpus[i + 1].frequency = frequency;
+ numCPUsWithFrequency++;
+ totalFrequency += frequency;
+ }
- for (int i = 0; i <= cpus; i++) {
- CPUData* cpuData = &(this->cpus[i]);
- cpuData->frequency = -1;
+ fclose(file);
}
+ if (numCPUsWithFrequency > 0)
+ this->cpus[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
+
+ return 0;
+}
+
+static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
+ FILE* file = fopen(PROCCPUINFOFILE, "r");
+ if (file == NULL)
+ return;
+
+ int cpus = this->super.cpuCount;
int numCPUsWithFrequency = 0;
double totalFrequency = 0;
+ int cpuid = -1;
- if (settings->showCPUFrequency) {
- FILE* file = fopen(PROCCPUINFOFILE, "r");
- if (file == NULL) {
- CRT_fatalError("Cannot open " PROCCPUINFOFILE);
- }
-
- int cpuid = -1;
+ while (!feof(file)) {
double frequency;
- while (!feof(file)) {
- char buffer[PROC_LINE_LENGTH];
- char *ok = fgets(buffer, PROC_LINE_LENGTH, file);
- if (!ok) break;
-
- if (
- (sscanf(buffer, "processor : %d", &cpuid) == 1) ||
- (sscanf(buffer, "processor: %d", &cpuid) == 1)
- ) {
- if (cpuid < 0 || cpuid > (cpus - 1)) {
- char tmpbuffer[64];
- xSnprintf(tmpbuffer, sizeof(tmpbuffer), PROCCPUINFOFILE " contains out-of-range CPU number %d", cpuid);
- CRT_fatalError(tmpbuffer);
- }
- } else if (
- (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
- (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1)
- ) {
- if (cpuid < 0 || cpuid > (cpus - 1)) {
- CRT_fatalError(PROCCPUINFOFILE " is malformed: cpu MHz line without corresponding processor line");
- }
+ char buffer[PROC_LINE_LENGTH];
+
+ if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
+ break;
+
+ if (
+ (sscanf(buffer, "processor : %d", &cpuid) == 1) ||
+ (sscanf(buffer, "processor: %d", &cpuid) == 1)
+ ) {
+ continue;
+ } else if (
+ (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
+ (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1)
+ ) {
+ if (cpuid < 0 || cpuid > (cpus - 1)) {
+ continue;
+ }
- int cpu = cpuid + 1;
- CPUData* cpuData = &(this->cpus[cpu]);
+ CPUData* cpuData = &(this->cpus[cpuid + 1]);
+ /* do not override sysfs data */
+ if (isnan(cpuData->frequency)) {
cpuData->frequency = frequency;
- numCPUsWithFrequency++;
- totalFrequency += frequency;
- } else if (buffer[0] == '\n') {
- cpuid = -1;
}
+ numCPUsWithFrequency++;
+ totalFrequency += frequency;
+ } else if (buffer[0] == '\n') {
+ cpuid = -1;
}
- fclose(file);
+ }
+ fclose(file);
+
+ if (numCPUsWithFrequency > 0) {
+ this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency;
+ }
+}
+
+static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
+ int cpus = this->super.cpuCount;
+ assert(cpus > 0);
- if (numCPUsWithFrequency > 0) {
- this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency;
+ for (int i = 0; i <= cpus; i++) {
+ this->cpus[i].frequency = NAN;
+ }
+
+ if (scanCPUFreqencyFromSysCPUFreq(this) == 0) {
+ return;
+ }
+
+ scanCPUFreqencyFromCPUinfo(this);
+}
+
+#ifdef HAVE_SENSORS_SENSORS_H
+static void LinuxProcessList_scanCPUTemperature(LinuxProcessList* this) {
+ const int cpuCount = this->super.cpuCount;
+
+ for (int i = 0; i <= cpuCount; i++) {
+ this->cpus[i].temperature = NAN;
+ }
+
+ int r = LibSensors_getCPUTemperatures(this->cpus, cpuCount);
+
+ /* No temperature - nothing to do */
+ if (r <= 0)
+ return;
+
+ /* Only package temperature - copy to all cpus */
+ if (r == 1 && !isnan(this->cpus[0].temperature)) {
+ double packageTemp = this->cpus[0].temperature;
+ for (int i = 1; i <= cpuCount; i++) {
+ this->cpus[i].temperature = packageTemp;
}
+
+ return;
}
- double period = (double)this->cpus[0].totalPeriod / cpus;
- return period;
+ /* Half the temperatures, probably HT/SMT - copy to second half */
+ if (r >= 2 && (r - 1) == (cpuCount / 2)) {
+ for (int i = cpuCount / 2 + 1; i <= cpuCount; i++) {
+ this->cpus[i].temperature = this->cpus[i/2].temperature;
+ }
+
+ return;
+ }
}
+#endif
-void ProcessList_goThroughEntries(ProcessList* super) {
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
LinuxProcessList* this = (LinuxProcessList*) super;
+ const Settings* settings = super->settings;
LinuxProcessList_scanMemoryInfo(super);
LinuxProcessList_scanZfsArcstats(this);
+ LinuxProcessList_updateCPUcount(this);
+ LinuxProcessList_scanZramInfo(this);
+
double period = LinuxProcessList_scanCPUTime(this);
- LinuxProcessList_scanCPUFrequency(this);
+ if (settings->showCPUFrequency) {
+ LinuxProcessList_scanCPUFrequency(this);
+ }
+
+ #ifdef HAVE_SENSORS_SENSORS_H
+ if (settings->showCPUTemperature)
+ LinuxProcessList_scanCPUTemperature(this);
+ #endif
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
struct timeval tv;
gettimeofday(&tv, NULL);
- LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv);
+ unsigned long long now = tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
+
+ /* PROCDIR is an absolute path */
+ assert(PROCDIR[0] == '/');
+#ifdef HAVE_OPENAT
+ openat_arg_t rootFd = AT_FDCWD;
+#else
+ openat_arg_t rootFd = "";
+#endif
+
+ LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period, now);
}

© 2014-2024 Faster IT GmbH | imprint | privacy policy