2014-11-24 20:55:49 +00:00
|
|
|
/*
|
|
|
|
htop - LinuxProcessList.c
|
|
|
|
(C) 2014 Hisham H. Muhammad
|
2021-09-22 09:33:00 +00:00
|
|
|
Released under the GNU GPLv2+, see the COPYING file
|
2014-11-24 20:55:49 +00:00
|
|
|
in the source distribution for its full text.
|
|
|
|
*/
|
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
2021-04-29 18:13:36 +00:00
|
|
|
#include "linux/LinuxProcessList.h"
|
2020-10-14 18:21:09 +00:00
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <assert.h>
|
2014-11-24 21:22:50 +00:00
|
|
|
#include <dirent.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2020-12-06 14:22:41 +00:00
|
|
|
#include <limits.h>
|
2014-11-24 21:22:50 +00:00
|
|
|
#include <math.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <stdbool.h>
|
2020-12-06 14:22:41 +00:00
|
|
|
#include <stdint.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2014-11-24 21:22:50 +00:00
|
|
|
#include <string.h>
|
2021-04-29 15:12:43 +00:00
|
|
|
#include <strings.h>
|
2021-01-13 14:44:05 +00:00
|
|
|
#include <time.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
2014-11-24 21:22:50 +00:00
|
|
|
#include <sys/types.h>
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2017-12-04 02:15:29 +00:00
|
|
|
#ifdef HAVE_DELAYACCT
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <linux/netlink.h>
|
|
|
|
#include <linux/taskstats.h>
|
2017-12-04 02:15:29 +00:00
|
|
|
#include <netlink/attr.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <netlink/handlers.h>
|
|
|
|
#include <netlink/msg.h>
|
2017-12-04 02:15:29 +00:00
|
|
|
#include <netlink/netlink.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <netlink/socket.h>
|
2017-12-04 02:15:29 +00:00
|
|
|
#include <netlink/genl/genl.h>
|
|
|
|
#include <netlink/genl/ctrl.h>
|
|
|
|
#endif
|
|
|
|
|
2020-11-07 21:51:46 +00:00
|
|
|
#include "Compat.h"
|
2020-11-20 16:50:34 +00:00
|
|
|
#include "CRT.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "Macros.h"
|
|
|
|
#include "Object.h"
|
|
|
|
#include "Process.h"
|
|
|
|
#include "Settings.h"
|
|
|
|
#include "XUtils.h"
|
2021-10-19 21:36:31 +00:00
|
|
|
#include "linux/CGroupUtils.h"
|
2021-04-29 18:13:36 +00:00
|
|
|
#include "linux/LinuxProcess.h"
|
2021-08-24 15:27:43 +00:00
|
|
|
#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep
|
2020-09-19 11:55:23 +00:00
|
|
|
|
2021-03-20 10:21:20 +00:00
|
|
|
#if defined(MAJOR_IN_MKDEV)
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <sys/mkdev.h>
|
|
|
|
#elif defined(MAJOR_IN_SYSMACROS)
|
|
|
|
#include <sys/sysmacros.h>
|
|
|
|
#endif
|
|
|
|
|
2020-12-01 12:59:19 +00:00
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H
|
|
|
|
#include "LibSensors.h"
|
2020-09-10 17:56:33 +00:00
|
|
|
#endif
|
|
|
|
|
2021-09-24 18:31:48 +00:00
|
|
|
#ifndef O_PATH
|
|
|
|
#define O_PATH 010000000 // declare for ancient glibc versions
|
|
|
|
#endif
|
|
|
|
|
2021-11-08 14:42:12 +00:00
|
|
|
/* Not exposed yet. Defined at include/linux/sched.h */
|
|
|
|
#ifndef PF_KTHREAD
|
|
|
|
#define PF_KTHREAD 0x00200000
|
|
|
|
#endif
|
2020-09-19 11:55:23 +00:00
|
|
|
|
2021-02-15 08:32:55 +00:00
|
|
|
static long long btime = -1;
|
2020-12-14 00:19:54 +00:00
|
|
|
|
|
|
|
static long jiffy;
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
|
2020-11-17 18:46:12 +00:00
|
|
|
assert(String_eq(mode, "r")); /* only currently supported mode */
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
int fd = Compat_openat(openatArg, pathname, O_RDONLY);
|
2020-11-17 18:46:12 +00:00
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
FILE* stream = fdopen(fd, mode);
|
|
|
|
if (!stream)
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
2016-10-01 06:09:04 +00:00
|
|
|
static int sortTtyDrivers(const void* va, const void* vb) {
|
2020-09-23 12:15:51 +00:00
|
|
|
const TtyDriver* a = (const TtyDriver*) va;
|
|
|
|
const TtyDriver* b = (const TtyDriver*) vb;
|
2020-11-15 19:12:38 +00:00
|
|
|
|
|
|
|
int r = SPACESHIP_NUMBER(a->major, b->major);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return SPACESHIP_NUMBER(a->minorFrom, b->minorFrom);
|
2016-10-01 06:09:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
|
|
|
|
TtyDriver* ttyDrivers;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2020-11-25 11:42:36 +00:00
|
|
|
char buf[16384];
|
|
|
|
ssize_t r = xReadfile(PROCTTYDRIVERSFILE, buf, sizeof(buf));
|
|
|
|
if (r < 0)
|
2016-10-01 06:09:04 +00:00
|
|
|
return;
|
2020-11-25 11:42:36 +00:00
|
|
|
|
2016-10-01 06:09:04 +00:00
|
|
|
int numDrivers = 0;
|
|
|
|
int allocd = 10;
|
2020-12-23 20:52:40 +00:00
|
|
|
ttyDrivers = xMallocArray(allocd, sizeof(TtyDriver));
|
2016-10-01 06:09:04 +00:00
|
|
|
char* at = buf;
|
|
|
|
while (*at != '\0') {
|
|
|
|
at = strchr(at, ' '); // skip first token
|
|
|
|
while (*at == ' ') at++; // skip spaces
|
2021-01-05 22:42:55 +00:00
|
|
|
const char* token = at; // mark beginning of path
|
2016-10-01 06:09:04 +00:00
|
|
|
at = strchr(at, ' '); // find end of path
|
|
|
|
*at = '\0'; at++; // clear and skip
|
2020-09-21 12:27:32 +00:00
|
|
|
ttyDrivers[numDrivers].path = xStrdup(token); // save
|
2016-10-01 06:09:04 +00:00
|
|
|
while (*at == ' ') at++; // skip spaces
|
|
|
|
token = at; // mark beginning of major
|
|
|
|
at = strchr(at, ' '); // find end of major
|
|
|
|
*at = '\0'; at++; // clear and skip
|
|
|
|
ttyDrivers[numDrivers].major = atoi(token); // save
|
|
|
|
while (*at == ' ') at++; // skip spaces
|
|
|
|
token = at; // mark beginning of minorFrom
|
|
|
|
while (*at >= '0' && *at <= '9') at++; //find end of minorFrom
|
|
|
|
if (*at == '-') { // if has range
|
|
|
|
*at = '\0'; at++; // clear and skip
|
|
|
|
ttyDrivers[numDrivers].minorFrom = atoi(token); // save
|
|
|
|
token = at; // mark beginning of minorTo
|
|
|
|
at = strchr(at, ' '); // find end of minorTo
|
|
|
|
*at = '\0'; at++; // clear and skip
|
|
|
|
ttyDrivers[numDrivers].minorTo = atoi(token); // save
|
|
|
|
} else { // no range
|
|
|
|
*at = '\0'; at++; // clear and skip
|
|
|
|
ttyDrivers[numDrivers].minorFrom = atoi(token); // save
|
|
|
|
ttyDrivers[numDrivers].minorTo = atoi(token); // save
|
|
|
|
}
|
|
|
|
at = strchr(at, '\n'); // go to end of line
|
|
|
|
at++; // skip
|
|
|
|
numDrivers++;
|
|
|
|
if (numDrivers == allocd) {
|
|
|
|
allocd += 10;
|
2020-12-23 20:52:40 +00:00
|
|
|
ttyDrivers = xReallocArray(ttyDrivers, allocd, sizeof(TtyDriver));
|
2016-10-01 06:09:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
numDrivers++;
|
2020-09-21 12:27:32 +00:00
|
|
|
ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
|
2016-10-01 06:09:04 +00:00
|
|
|
ttyDrivers[numDrivers - 1].path = NULL;
|
|
|
|
qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers);
|
|
|
|
this->ttyDrivers = ttyDrivers;
|
|
|
|
}
|
|
|
|
|
2017-12-04 02:15:29 +00:00
|
|
|
#ifdef HAVE_DELAYACCT
|
|
|
|
|
|
|
|
static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
|
|
|
|
this->netlink_socket = nl_socket_alloc();
|
|
|
|
if (this->netlink_socket == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
static void LinuxProcessList_updateCPUcount(ProcessList* super) {
|
2021-08-08 13:10:34 +00:00
|
|
|
/* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF
|
2021-06-12 16:17:28 +00:00
|
|
|
* https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD
|
|
|
|
*/
|
|
|
|
|
2021-02-17 15:26:10 +00:00
|
|
|
LinuxProcessList* this = (LinuxProcessList*) super;
|
2021-06-12 16:17:28 +00:00
|
|
|
unsigned int existing = 0, active = 0;
|
2020-09-22 18:04:41 +00:00
|
|
|
|
2022-03-26 12:57:36 +00:00
|
|
|
// Initialize the cpuData array before anything else.
|
|
|
|
if (!this->cpuData) {
|
2022-03-27 07:53:56 +00:00
|
|
|
this->cpuData = xCalloc(2, sizeof(CPUData));
|
2021-08-10 03:58:55 +00:00
|
|
|
this->cpuData[0].online = true; /* average is always "online" */
|
2021-06-12 16:17:28 +00:00
|
|
|
this->cpuData[1].online = true;
|
2021-09-10 09:26:56 +00:00
|
|
|
super->activeCPUs = 1;
|
|
|
|
super->existingCPUs = 1;
|
2020-09-22 18:04:41 +00:00
|
|
|
}
|
|
|
|
|
2022-03-26 12:57:36 +00:00
|
|
|
DIR* dir = opendir("/sys/devices/system/cpu");
|
|
|
|
if (!dir)
|
|
|
|
return;
|
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
unsigned int currExisting = super->existingCPUs;
|
|
|
|
|
|
|
|
const struct dirent* entry;
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
2022-03-26 11:18:12 +00:00
|
|
|
if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
|
2021-06-12 16:17:28 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!String_startsWith(entry->d_name, "cpu"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
char *endp;
|
|
|
|
unsigned long int id = strtoul(entry->d_name + 3, &endp, 10);
|
|
|
|
if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0')
|
|
|
|
continue;
|
2020-09-22 18:04:41 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
#ifdef HAVE_OPENAT
|
|
|
|
int cpuDirFd = openat(dirfd(dir), entry->d_name, O_DIRECTORY | O_PATH | O_NOFOLLOW);
|
|
|
|
if (cpuDirFd < 0)
|
|
|
|
continue;
|
|
|
|
#else
|
|
|
|
char cpuDirFd[4096];
|
|
|
|
xSnprintf(cpuDirFd, sizeof(cpuDirFd), "/sys/devices/system/cpu/%s", entry->d_name);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
existing++;
|
2020-09-22 18:04:41 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
/* readdir() iterates with no specific order */
|
|
|
|
unsigned int max = MAXIMUM(existing, id + 1);
|
|
|
|
if (max > currExisting) {
|
2021-09-10 09:26:56 +00:00
|
|
|
this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData));
|
2021-08-10 03:58:55 +00:00
|
|
|
this->cpuData[0].online = true; /* average is always "online" */
|
2021-06-12 16:17:28 +00:00
|
|
|
currExisting = max;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[8];
|
|
|
|
ssize_t res = xReadfileat(cpuDirFd, "online", buffer, sizeof(buffer));
|
|
|
|
/* If the file "online" does not exist or on failure count as active */
|
|
|
|
if (res < 1 || buffer[0] != '0') {
|
|
|
|
active++;
|
|
|
|
this->cpuData[id + 1].online = true;
|
|
|
|
} else {
|
|
|
|
this->cpuData[id + 1].online = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Compat_openatArgClose(cpuDirFd);
|
2020-09-22 18:04:41 +00:00
|
|
|
}
|
2021-06-12 16:17:28 +00:00
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
2022-03-26 12:57:36 +00:00
|
|
|
// return if no CPU is found
|
|
|
|
if (existing < 1)
|
|
|
|
return;
|
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H
|
|
|
|
/* When started with offline CPUs, libsensors does not monitor those,
|
|
|
|
* even when they become online. */
|
|
|
|
if (super->existingCPUs != 0 && (active > super->activeCPUs || currExisting > super->existingCPUs))
|
|
|
|
LibSensors_reload();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
super->activeCPUs = active;
|
|
|
|
assert(existing == currExisting);
|
|
|
|
super->existingCPUs = currExisting;
|
2020-09-22 18:04:41 +00:00
|
|
|
}
|
|
|
|
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
|
2016-02-02 14:53:02 +00:00
|
|
|
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
|
2015-01-22 01:27:31 +00:00
|
|
|
ProcessList* pl = &(this->super);
|
2018-08-19 04:29:03 +00:00
|
|
|
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
ProcessList_init(pl, Class(LinuxProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId);
|
2016-10-01 06:09:04 +00:00
|
|
|
LinuxProcessList_initTtyDrivers(this);
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2020-12-10 00:57:48 +00:00
|
|
|
// Initialize page size
|
|
|
|
pageSize = sysconf(_SC_PAGESIZE);
|
|
|
|
if (pageSize == -1)
|
|
|
|
CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
|
|
|
|
pageSizeKB = pageSize / ONE_K;
|
|
|
|
|
2020-12-14 00:19:54 +00:00
|
|
|
// Initialize clock ticks
|
|
|
|
jiffy = sysconf(_SC_CLK_TCK);
|
|
|
|
if (jiffy == -1)
|
|
|
|
CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
|
|
|
|
|
2020-12-14 00:27:13 +00:00
|
|
|
// Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+)
|
|
|
|
this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0);
|
2018-10-16 18:08:23 +00:00
|
|
|
|
2020-12-14 01:16:32 +00:00
|
|
|
// Read btime (the kernel boot time, as number of seconds since the epoch)
|
2021-02-17 15:26:10 +00:00
|
|
|
FILE* statfile = fopen(PROCSTATFILE, "r");
|
|
|
|
if (statfile == NULL)
|
|
|
|
CRT_fatalError("Cannot open " PROCSTATFILE);
|
|
|
|
while (true) {
|
|
|
|
char buffer[PROC_LINE_LENGTH + 1];
|
|
|
|
if (fgets(buffer, sizeof(buffer), statfile) == NULL)
|
|
|
|
break;
|
|
|
|
if (String_startsWith(buffer, "btime ") == false)
|
|
|
|
continue;
|
|
|
|
if (sscanf(buffer, "btime %lld\n", &btime) == 1)
|
|
|
|
break;
|
|
|
|
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
|
2020-09-22 18:04:41 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
fclose(statfile);
|
|
|
|
|
2021-02-17 15:26:10 +00:00
|
|
|
if (btime == -1)
|
|
|
|
CRT_fatalError("No btime in " PROCSTATFILE);
|
|
|
|
|
2020-11-18 12:59:55 +00:00
|
|
|
// Initialize CPU count
|
2021-06-12 16:17:28 +00:00
|
|
|
LinuxProcessList_updateCPUcount(pl);
|
2020-09-22 18:04:41 +00:00
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
return pl;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
void ProcessList_delete(ProcessList* pl) {
|
|
|
|
LinuxProcessList* this = (LinuxProcessList*) pl;
|
|
|
|
ProcessList_done(pl);
|
2021-06-12 16:17:28 +00:00
|
|
|
free(this->cpuData);
|
2016-10-01 06:09:04 +00:00
|
|
|
if (this->ttyDrivers) {
|
2020-10-31 19:55:36 +00:00
|
|
|
for (int i = 0; this->ttyDrivers[i].path; i++) {
|
2016-10-01 06:09:04 +00:00
|
|
|
free(this->ttyDrivers[i].path);
|
|
|
|
}
|
|
|
|
free(this->ttyDrivers);
|
|
|
|
}
|
2017-12-04 02:15:29 +00:00
|
|
|
#ifdef HAVE_DELAYACCT
|
|
|
|
if (this->netlink_socket) {
|
|
|
|
nl_close(this->netlink_socket);
|
|
|
|
nl_socket_free(this->netlink_socket);
|
|
|
|
}
|
|
|
|
#endif
|
2014-11-27 19:48:38 +00:00
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
2020-12-14 00:19:54 +00:00
|
|
|
static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) {
|
2020-11-23 20:03:52 +00:00
|
|
|
return t * 100 / jiffy;
|
2015-03-17 02:01:48 +00:00
|
|
|
}
|
|
|
|
|
2021-10-11 22:45:09 +00:00
|
|
|
/* Taken from: https://github.com/torvalds/linux/blob/64570fbc14f8d7cb3fe3995f20e26bc25ce4b2cc/fs/proc/array.c#L120 */
|
|
|
|
static inline ProcessState LinuxProcessList_getProcessState(char state) {
|
|
|
|
switch (state) {
|
|
|
|
case 'S': return SLEEPING;
|
|
|
|
case 'X': return DEFUNCT;
|
|
|
|
case 'Z': return ZOMBIE;
|
|
|
|
case 't': return TRACED;
|
|
|
|
case 'T': return STOPPED;
|
|
|
|
case 'D': return UNINTERRUPTIBLE_WAIT;
|
|
|
|
case 'R': return RUNNING;
|
|
|
|
case 'P': return BLOCKED;
|
|
|
|
case 'I': return IDLE;
|
|
|
|
default: return UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-05 13:53:07 +00:00
|
|
|
static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) {
|
2015-03-16 04:43:04 +00:00
|
|
|
LinuxProcess* lp = (LinuxProcess*) process;
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2020-11-25 11:42:36 +00:00
|
|
|
char buf[MAX_READ + 1];
|
|
|
|
ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf));
|
|
|
|
if (r < 0)
|
2020-11-01 00:09:51 +00:00
|
|
|
return false;
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-01-27 14:12:08 +00:00
|
|
|
/* (1) pid - %d */
|
2014-11-24 20:55:49 +00:00
|
|
|
assert(process->pid == atoi(buf));
|
2020-10-31 22:28:02 +00:00
|
|
|
char* location = strchr(buf, ' ');
|
2020-11-01 00:09:51 +00:00
|
|
|
if (!location)
|
|
|
|
return false;
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-01-27 14:12:08 +00:00
|
|
|
/* (2) comm - (%s) */
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 2;
|
2020-10-31 22:28:02 +00:00
|
|
|
char* end = strrchr(location, ')');
|
2020-11-01 00:09:51 +00:00
|
|
|
if (!end)
|
|
|
|
return false;
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2021-01-05 13:53:07 +00:00
|
|
|
String_safeStrncpy(command, location, MINIMUM((size_t)(end - location + 1), commLen));
|
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
location = end + 2;
|
|
|
|
|
2021-01-27 14:12:08 +00:00
|
|
|
/* (3) state - %c */
|
2021-10-11 22:45:09 +00:00
|
|
|
process->state = LinuxProcessList_getProcessState(location[0]);
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 2;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (4) ppid - %d */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->ppid = strtol(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (5) pgrp - %d */
|
2021-01-27 14:12:15 +00:00
|
|
|
process->pgrp = strtol(location, &location, 10);
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (6) session - %d */
|
2021-01-27 14:12:15 +00:00
|
|
|
process->session = strtol(location, &location, 10);
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (7) tty_nr - %d */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->tty_nr = strtoul(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (8) tpgid - %d */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->tpgid = strtol(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:11:37 +00:00
|
|
|
|
2021-11-08 14:42:12 +00:00
|
|
|
/* (9) flags - %u */
|
|
|
|
lp->flags = strtoul(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:11:37 +00:00
|
|
|
|
2021-01-27 14:12:08 +00:00
|
|
|
/* (10) minflt - %lu */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->minflt = strtoull(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (11) cminflt - %lu */
|
2015-03-16 04:43:04 +00:00
|
|
|
lp->cminflt = strtoull(location, &location, 10);
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (12) majflt - %lu */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->majflt = strtoull(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (13) cmajflt - %lu */
|
2015-03-16 04:43:04 +00:00
|
|
|
lp->cmajflt = strtoull(location, &location, 10);
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (14) utime - %lu */
|
2020-12-14 00:19:54 +00:00
|
|
|
lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (15) stime - %lu */
|
2020-12-14 00:19:54 +00:00
|
|
|
lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (16) cutime - %ld */
|
2020-12-14 00:19:54 +00:00
|
|
|
lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (17) cstime - %ld */
|
2020-12-14 00:19:54 +00:00
|
|
|
lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
|
2014-11-24 20:55:49 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (18) priority - %ld */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->priority = strtol(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (19) nice - %ld */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->nice = strtol(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (20) num_threads - %ld */
|
2014-11-24 20:55:49 +00:00
|
|
|
process->nlwp = strtol(location, &location, 10);
|
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* Skip (21) itrealvalue - %ld */
|
2020-10-31 22:28:02 +00:00
|
|
|
location = strchr(location, ' ') + 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (22) starttime - %llu */
|
2020-10-12 10:51:18 +00:00
|
|
|
if (process->starttime_ctime == 0) {
|
2020-12-14 00:19:54 +00:00
|
|
|
process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100;
|
2020-10-12 10:51:18 +00:00
|
|
|
} else {
|
2021-01-27 14:12:08 +00:00
|
|
|
location = strchr(location, ' ');
|
2020-10-12 10:51:18 +00:00
|
|
|
}
|
2018-08-19 04:29:03 +00:00
|
|
|
location += 1;
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* Skip (23) - (38) */
|
|
|
|
for (int i = 0; i < 16; i++) {
|
2020-10-31 22:28:02 +00:00
|
|
|
location = strchr(location, ' ') + 1;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2021-01-27 14:11:37 +00:00
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
assert(location != NULL);
|
2021-01-27 14:12:08 +00:00
|
|
|
|
|
|
|
/* (39) processor - %d */
|
2015-03-17 02:03:40 +00:00
|
|
|
process->processor = strtol(location, &location, 10);
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2021-01-27 14:12:08 +00:00
|
|
|
/* Ignore further fields */
|
|
|
|
|
2015-03-16 04:43:04 +00:00
|
|
|
process->time = lp->utime + lp->stime;
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-09 09:13:39 +00:00
|
|
|
static bool LinuxProcessList_updateUser(ProcessList* processList, Process* process, openat_arg_t procFd) {
|
2014-11-24 20:55:49 +00:00
|
|
|
struct stat sstat;
|
2020-11-20 16:50:34 +00:00
|
|
|
#ifdef HAVE_OPENAT
|
2020-11-17 18:46:12 +00:00
|
|
|
int statok = fstat(procFd, &sstat);
|
2020-11-20 16:50:34 +00:00
|
|
|
#else
|
|
|
|
int statok = stat(procFd, &sstat);
|
|
|
|
#endif
|
2014-11-24 20:55:49 +00:00
|
|
|
if (statok == -1)
|
|
|
|
return false;
|
2021-06-09 09:13:39 +00:00
|
|
|
|
|
|
|
if (process->st_uid != sstat.st_uid) {
|
|
|
|
process->st_uid = sstat.st_uid;
|
|
|
|
process->user = UsersTable_getRef(processList->usersTable, sstat.st_uid);
|
|
|
|
}
|
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-04-26 15:57:47 +00:00
|
|
|
static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long realtimeMs) {
|
2020-11-25 11:42:36 +00:00
|
|
|
char buffer[1024];
|
|
|
|
ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer));
|
|
|
|
if (r < 0) {
|
2020-09-07 09:53:58 +00:00
|
|
|
process->io_rate_read_bps = NAN;
|
|
|
|
process->io_rate_write_bps = NAN;
|
2021-01-27 14:11:48 +00:00
|
|
|
process->io_rchar = ULLONG_MAX;
|
|
|
|
process->io_wchar = ULLONG_MAX;
|
|
|
|
process->io_syscr = ULLONG_MAX;
|
|
|
|
process->io_syscw = ULLONG_MAX;
|
|
|
|
process->io_read_bytes = ULLONG_MAX;
|
|
|
|
process->io_write_bytes = ULLONG_MAX;
|
|
|
|
process->io_cancelled_write_bytes = ULLONG_MAX;
|
2021-04-26 15:57:47 +00:00
|
|
|
process->io_last_scan_time_ms = realtimeMs;
|
2014-11-24 20:55:49 +00:00
|
|
|
return;
|
2015-01-22 01:27:31 +00:00
|
|
|
}
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
unsigned long long last_read = process->io_read_bytes;
|
|
|
|
unsigned long long last_write = process->io_write_bytes;
|
2022-02-03 16:48:18 +00:00
|
|
|
unsigned long long time_delta = realtimeMs > process->io_last_scan_time_ms ? realtimeMs - process->io_last_scan_time_ms : 0;
|
|
|
|
|
2020-10-31 22:28:02 +00:00
|
|
|
char* buf = buffer;
|
2021-01-05 22:42:55 +00:00
|
|
|
const char* line;
|
2014-11-24 20:55:49 +00:00
|
|
|
while ((line = strsep(&buf, "\n")) != NULL) {
|
|
|
|
switch (line[0]) {
|
|
|
|
case 'r':
|
2020-10-31 21:14:27 +00:00
|
|
|
if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
|
2021-04-26 15:57:47 +00:00
|
|
|
process->io_rchar = strtoull(line + 7, NULL, 10);
|
2020-10-31 21:14:27 +00:00
|
|
|
} else if (String_startsWith(line + 1, "ead_bytes: ")) {
|
2021-04-26 15:57:47 +00:00
|
|
|
process->io_read_bytes = strtoull(line + 12, NULL, 10);
|
2022-02-03 16:48:18 +00:00
|
|
|
process->io_rate_read_bps = time_delta ? (process->io_read_bytes - last_read) * /*ms to s*/1000. / time_delta : NAN;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'w':
|
2020-10-31 21:14:27 +00:00
|
|
|
if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
|
2021-04-26 15:57:47 +00:00
|
|
|
process->io_wchar = strtoull(line + 7, NULL, 10);
|
2020-10-31 21:14:27 +00:00
|
|
|
} else if (String_startsWith(line + 1, "rite_bytes: ")) {
|
2021-04-26 15:57:47 +00:00
|
|
|
process->io_write_bytes = strtoull(line + 13, NULL, 10);
|
2022-02-03 16:48:18 +00:00
|
|
|
process->io_rate_write_bps = time_delta ? (process->io_write_bytes - last_write) * /*ms to s*/1000. / time_delta : NAN;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 's':
|
2020-10-31 22:28:02 +00:00
|
|
|
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);
|
2016-02-16 16:34:25 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
break;
|
|
|
|
case 'c':
|
2020-10-31 21:14:27 +00:00
|
|
|
if (String_startsWith(line + 1, "ancelled_write_bytes: ")) {
|
2021-04-26 15:57:47 +00:00
|
|
|
process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10);
|
2020-10-31 20:10:12 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-27 14:11:48 +00:00
|
|
|
|
2021-04-26 15:57:47 +00:00
|
|
|
process->io_last_scan_time_ms = realtimeMs;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 19:25:54 +00:00
|
|
|
typedef struct LibraryData_ {
|
2021-07-14 17:15:09 +00:00
|
|
|
uint64_t size;
|
|
|
|
bool exec;
|
2020-10-09 13:13:06 +00:00
|
|
|
} LibraryData;
|
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
static inline uint64_t fast_strtoull_dec(char** str, int maxlen) {
|
2020-11-18 21:02:44 +00:00
|
|
|
register uint64_t result = 0;
|
|
|
|
|
|
|
|
if (!maxlen)
|
|
|
|
--maxlen;
|
|
|
|
|
|
|
|
while (maxlen-- && **str >= '0' && **str <= '9') {
|
|
|
|
result *= 10;
|
|
|
|
result += **str - '0';
|
|
|
|
(*str)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
static inline uint64_t fast_strtoull_hex(char** str, int maxlen) {
|
2020-11-18 21:02:44 +00:00
|
|
|
register uint64_t result = 0;
|
|
|
|
register int nibble, letter;
|
|
|
|
const long valid_mask = 0x03FF007E;
|
|
|
|
|
|
|
|
if (!maxlen)
|
|
|
|
--maxlen;
|
|
|
|
|
|
|
|
while (maxlen--) {
|
2020-12-02 15:08:50 +00:00
|
|
|
nibble = (unsigned char)**str;
|
2020-11-18 21:02:44 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-01-04 22:20:36 +00:00
|
|
|
static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* value, void* data) {
|
2020-10-09 13:13:06 +00:00
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return;
|
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
const LibraryData* v = (const LibraryData*)value;
|
|
|
|
uint64_t* d = (uint64_t*)data;
|
2020-10-09 13:13:06 +00:00
|
|
|
if (!v->exec)
|
|
|
|
return;
|
|
|
|
|
|
|
|
*d += v->size;
|
|
|
|
}
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd, bool calcSize, bool checkDeletedLib) {
|
|
|
|
Process* proc = (Process*)process;
|
|
|
|
|
|
|
|
proc->usesDeletedLib = false;
|
|
|
|
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* mapsfile = fopenat(procFd, "maps", "r");
|
2020-10-09 13:13:06 +00:00
|
|
|
if (!mapsfile)
|
2021-04-04 16:07:26 +00:00
|
|
|
return;
|
2020-10-09 13:13:06 +00:00
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
Hashtable* ht = NULL;
|
|
|
|
if (calcSize)
|
|
|
|
ht = Hashtable_new(64, true);
|
2020-10-09 13:13:06 +00:00
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
while (fgets(buffer, sizeof(buffer), mapsfile)) {
|
|
|
|
uint64_t map_start;
|
|
|
|
uint64_t map_end;
|
2021-04-04 16:07:26 +00:00
|
|
|
bool map_execute;
|
2020-11-28 16:06:06 +00:00
|
|
|
unsigned int map_devmaj;
|
|
|
|
unsigned int map_devmin;
|
2020-10-09 13:13:06 +00:00
|
|
|
uint64_t map_inode;
|
|
|
|
|
2020-11-17 17:04:30 +00:00
|
|
|
// Short circuit test: Look for a slash
|
|
|
|
if (!strchr(buffer, '/'))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Parse format: "%Lx-%Lx %4s %x %2x:%2x %Ld"
|
2021-07-14 17:24:18 +00:00
|
|
|
char* readptr = buffer;
|
2020-11-17 17:04:30 +00:00
|
|
|
|
2020-11-18 21:02:44 +00:00
|
|
|
map_start = fast_strtoull_hex(&readptr, 16);
|
|
|
|
if ('-' != *readptr++)
|
2020-11-17 17:04:30 +00:00
|
|
|
continue;
|
|
|
|
|
2020-11-18 21:02:44 +00:00
|
|
|
map_end = fast_strtoull_hex(&readptr, 16);
|
|
|
|
if (' ' != *readptr++)
|
2020-11-17 17:04:30 +00:00
|
|
|
continue;
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
map_execute = (readptr[2] == 'x');
|
2020-11-17 17:04:30 +00:00
|
|
|
readptr += 4;
|
|
|
|
if (' ' != *readptr++)
|
|
|
|
continue;
|
|
|
|
|
2020-11-18 21:02:44 +00:00
|
|
|
while(*readptr > ' ')
|
|
|
|
readptr++; // Skip parsing this hex value
|
|
|
|
if (' ' != *readptr++)
|
2020-11-17 17:04:30 +00:00
|
|
|
continue;
|
|
|
|
|
2020-11-28 16:06:06 +00:00
|
|
|
map_devmaj = fast_strtoull_hex(&readptr, 4);
|
2020-11-18 21:02:44 +00:00
|
|
|
if (':' != *readptr++)
|
2020-11-17 17:04:30 +00:00
|
|
|
continue;
|
|
|
|
|
2020-11-28 16:06:06 +00:00
|
|
|
map_devmin = fast_strtoull_hex(&readptr, 4);
|
2020-11-18 21:02:44 +00:00
|
|
|
if (' ' != *readptr++)
|
2020-11-17 17:04:30 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
//Minor shortcut: Once we know there's no file for this region, we skip
|
|
|
|
if (!map_devmaj && !map_devmin)
|
|
|
|
continue;
|
|
|
|
|
2020-11-18 21:02:44 +00:00
|
|
|
map_inode = fast_strtoull_dec(&readptr, 20);
|
2020-10-09 13:13:06 +00:00
|
|
|
if (!map_inode)
|
|
|
|
continue;
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
if (calcSize) {
|
|
|
|
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 |= map_execute;
|
2020-10-09 13:13:06 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
if (checkDeletedLib && map_execute && !proc->usesDeletedLib) {
|
|
|
|
while (*readptr == ' ')
|
|
|
|
readptr++;
|
|
|
|
|
|
|
|
if (*readptr != '/')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (String_startsWith(readptr, "/memfd:"))
|
|
|
|
continue;
|
|
|
|
|
2021-10-25 10:47:30 +00:00
|
|
|
/* Virtualbox maps /dev/zero for memory allocation. That results in
|
|
|
|
* false positive, so ignore. */
|
|
|
|
if (String_eq(readptr, "/dev/zero (deleted)\n"))
|
|
|
|
continue;
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
if (strstr(readptr, " (deleted)\n")) {
|
|
|
|
proc->usesDeletedLib = true;
|
|
|
|
if (!calcSize)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-10-09 13:13:06 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 17:04:30 +00:00
|
|
|
fclose(mapsfile);
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
if (calcSize) {
|
|
|
|
uint64_t total_size = 0;
|
|
|
|
Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size);
|
2020-10-09 13:13:06 +00:00
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
Hashtable_delete(ht);
|
2020-10-09 13:13:06 +00:00
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
process->m_lrs = total_size / pageSize;
|
|
|
|
}
|
2020-10-09 13:13:06 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd) {
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* statmfile = fopenat(procFd, "statm", "r");
|
2020-09-28 10:01:56 +00:00
|
|
|
if (!statmfile)
|
2014-11-24 20:55:49 +00:00
|
|
|
return false;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-08-28 15:57:39 +00:00
|
|
|
long int dummy, dummy2;
|
2021-06-12 18:34:42 +00:00
|
|
|
|
2020-09-28 10:01:56 +00:00
|
|
|
int r = fscanf(statmfile, "%ld %ld %ld %ld %ld %ld %ld",
|
2020-11-20 16:09:34 +00:00
|
|
|
&process->super.m_virt,
|
2020-09-28 10:01:56 +00:00
|
|
|
&process->super.m_resident,
|
|
|
|
&process->m_share,
|
|
|
|
&process->m_trs,
|
2021-06-12 18:34:42 +00:00
|
|
|
&dummy, /* unused since Linux 2.6; always 0 */
|
2020-09-28 10:01:56 +00:00
|
|
|
&process->m_drs,
|
2021-08-28 15:57:39 +00:00
|
|
|
&dummy2); /* unused since Linux 2.6; always 0 */
|
2020-09-28 10:01:56 +00:00
|
|
|
fclose(statmfile);
|
2020-11-16 18:12:41 +00:00
|
|
|
|
|
|
|
if (r == 7) {
|
2020-12-10 00:57:48 +00:00
|
|
|
process->super.m_virt *= pageSizeKB;
|
|
|
|
process->super.m_resident *= pageSizeKB;
|
2020-10-09 13:13:06 +00:00
|
|
|
}
|
2020-11-16 18:12:41 +00:00
|
|
|
|
2020-09-28 10:01:56 +00:00
|
|
|
return r == 7;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, bool haveSmapsRollup) {
|
2018-10-09 19:49:29 +00:00
|
|
|
//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.
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* f = fopenat(procFd, haveSmapsRollup ? "smaps_rollup" : "smaps", "r");
|
2020-10-27 18:30:05 +00:00
|
|
|
if (!f)
|
2018-10-09 19:49:29 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
process->m_pss = 0;
|
|
|
|
process->m_swap = 0;
|
|
|
|
process->m_psswp = 0;
|
|
|
|
|
2020-11-17 18:46:12 +00:00
|
|
|
char buffer[256];
|
2020-10-27 18:30:05 +00:00
|
|
|
while (fgets(buffer, sizeof(buffer), f)) {
|
2020-10-31 19:52:20 +00:00
|
|
|
if (!strchr(buffer, '\n')) {
|
2020-10-27 18:30:05 +00:00
|
|
|
// Partial line, skip to end of this line
|
2020-10-31 19:39:01 +00:00
|
|
|
while (fgets(buffer, sizeof(buffer), f)) {
|
2020-10-31 19:52:20 +00:00
|
|
|
if (strchr(buffer, '\n')) {
|
2020-10-27 18:30:05 +00:00
|
|
|
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);
|
2018-10-09 19:49:29 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
#ifdef HAVE_OPENVZ
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t procFd) {
|
2021-03-17 16:53:00 +00:00
|
|
|
if (access(PROCDIR "/vz", R_OK) != 0) {
|
2020-09-30 21:46:52 +00:00
|
|
|
free(process->ctid);
|
|
|
|
process->ctid = NULL;
|
2015-05-13 18:00:58 +00:00
|
|
|
process->vpid = process->super.pid;
|
2014-11-24 20:55:49 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-09-30 21:46:52 +00:00
|
|
|
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* file = fopenat(procFd, "status", "r");
|
2020-09-30 21:46:52 +00:00
|
|
|
if (!file) {
|
|
|
|
free(process->ctid);
|
|
|
|
process->ctid = NULL;
|
|
|
|
process->vpid = process->super.pid;
|
2014-11-24 20:55:49 +00:00
|
|
|
return;
|
2020-09-30 21:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool foundEnvID = false;
|
|
|
|
bool foundVPid = false;
|
|
|
|
char linebuf[256];
|
2020-10-31 19:39:01 +00:00
|
|
|
while (fgets(linebuf, sizeof(linebuf), file) != NULL) {
|
2020-10-31 19:52:20 +00:00
|
|
|
if (strchr(linebuf, '\n') == NULL) {
|
2020-09-30 21:46:52 +00:00
|
|
|
// Partial line, skip to end of this line
|
2020-10-31 19:39:01 +00:00
|
|
|
while (fgets(linebuf, sizeof(linebuf), file) != NULL) {
|
2020-10-31 19:52:20 +00:00
|
|
|
if (strchr(linebuf, '\n') != NULL) {
|
2020-09-30 21:46:52 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* name_value_sep = strchr(linebuf, ':');
|
2020-10-31 19:52:20 +00:00
|
|
|
if (name_value_sep == NULL) {
|
2020-09-30 21:46:52 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int field;
|
2020-10-31 19:52:20 +00:00
|
|
|
if (0 == strncasecmp(linebuf, "envID", name_value_sep - linebuf)) {
|
2020-09-30 21:46:52 +00:00
|
|
|
field = 1;
|
2020-10-31 19:52:20 +00:00
|
|
|
} else if (0 == strncasecmp(linebuf, "VPid", name_value_sep - linebuf)) {
|
2020-09-30 21:46:52 +00:00
|
|
|
field = 2;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
name_value_sep++;
|
2020-10-31 19:39:01 +00:00
|
|
|
} while (*name_value_sep != '\0' && *name_value_sep <= 32);
|
2020-09-30 21:46:52 +00:00
|
|
|
|
|
|
|
char* value_end = name_value_sep;
|
|
|
|
|
2020-11-15 13:16:23 +00:00
|
|
|
while(*value_end > 32) {
|
2020-09-30 21:46:52 +00:00
|
|
|
value_end++;
|
|
|
|
}
|
|
|
|
|
2020-10-31 19:52:20 +00:00
|
|
|
if (name_value_sep == value_end) {
|
2020-09-30 21:46:52 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
*value_end = '\0';
|
|
|
|
|
|
|
|
switch(field) {
|
|
|
|
case 1:
|
|
|
|
foundEnvID = true;
|
2021-01-05 13:47:49 +00:00
|
|
|
if (!String_eq(name_value_sep, process->ctid ? process->ctid : ""))
|
|
|
|
free_and_xStrdup(&process->ctid, name_value_sep);
|
2020-09-30 21:46:52 +00:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
fclose(file);
|
2020-09-30 21:46:52 +00:00
|
|
|
|
2020-10-31 19:52:20 +00:00
|
|
|
if (!foundEnvID) {
|
2020-09-30 21:46:52 +00:00
|
|
|
free(process->ctid);
|
|
|
|
process->ctid = NULL;
|
|
|
|
}
|
|
|
|
|
2020-10-31 19:52:20 +00:00
|
|
|
if (!foundVPid) {
|
2020-09-30 21:46:52 +00:00
|
|
|
process->vpid = process->super.pid;
|
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) {
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* file = fopenat(procFd, "cgroup", "r");
|
2014-11-24 20:55:49 +00:00
|
|
|
if (!file) {
|
2020-09-21 11:47:39 +00:00
|
|
|
if (process->cgroup) {
|
|
|
|
free(process->cgroup);
|
|
|
|
process->cgroup = NULL;
|
|
|
|
}
|
2021-10-19 21:36:31 +00:00
|
|
|
if (process->cgroup_short) {
|
|
|
|
free(process->cgroup_short);
|
|
|
|
process->cgroup_short = NULL;
|
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-12-14 15:27:11 +00:00
|
|
|
char output[PROC_LINE_LENGTH + 1];
|
2015-03-08 22:47:49 +00:00
|
|
|
output[0] = '\0';
|
|
|
|
char* at = output;
|
2015-12-14 15:27:11 +00:00
|
|
|
int left = PROC_LINE_LENGTH;
|
2015-03-08 22:47:49 +00:00
|
|
|
while (!feof(file) && left > 0) {
|
2015-12-14 15:27:11 +00:00
|
|
|
char buffer[PROC_LINE_LENGTH + 1];
|
2021-01-05 22:42:55 +00:00
|
|
|
const char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (!ok)
|
|
|
|
break;
|
|
|
|
|
2021-11-06 16:20:37 +00:00
|
|
|
char* group = buffer;
|
|
|
|
for (size_t i = 0; i < 2; i++) {
|
|
|
|
group = strchrnul(group, ':');
|
|
|
|
if (!*group)
|
|
|
|
break;
|
|
|
|
group++;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* eol = strchrnul(group, '\n');
|
|
|
|
*eol = '\0';
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2015-03-08 22:47:49 +00:00
|
|
|
if (at != output) {
|
|
|
|
*at = ';';
|
|
|
|
at++;
|
|
|
|
left--;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2017-08-01 22:48:43 +00:00
|
|
|
int wrote = snprintf(at, left, "%s", group);
|
2015-03-08 22:47:49 +00:00
|
|
|
left -= wrote;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
fclose(file);
|
2021-10-19 21:36:31 +00:00
|
|
|
|
|
|
|
bool changed = !process->cgroup || !String_eq(process->cgroup, output);
|
|
|
|
|
2021-12-09 17:14:49 +00:00
|
|
|
Process_updateFieldWidth(CGROUP, strlen(output));
|
2021-01-05 13:47:49 +00:00
|
|
|
free_and_xStrdup(&process->cgroup, output);
|
2021-10-19 21:36:31 +00:00
|
|
|
|
2021-12-09 17:14:49 +00:00
|
|
|
if (!changed) {
|
|
|
|
if(process->cgroup_short) {
|
|
|
|
Process_updateFieldWidth(CCGROUP, strlen(process->cgroup_short));
|
|
|
|
} else {
|
|
|
|
//CCGROUP is alias to normal CGROUP if shortening fails
|
|
|
|
Process_updateFieldWidth(CCGROUP, strlen(process->cgroup));
|
|
|
|
}
|
2021-10-19 21:36:31 +00:00
|
|
|
return;
|
2021-12-09 17:14:49 +00:00
|
|
|
}
|
2021-10-19 21:36:31 +00:00
|
|
|
|
2021-10-20 18:19:00 +00:00
|
|
|
char* cgroup_short = CGroup_filterName(process->cgroup);
|
|
|
|
if (cgroup_short) {
|
2021-12-09 17:14:49 +00:00
|
|
|
Process_updateFieldWidth(CCGROUP, strlen(cgroup_short));
|
2021-10-19 21:36:31 +00:00
|
|
|
free_and_xStrdup(&process->cgroup_short, cgroup_short);
|
2021-10-20 18:19:00 +00:00
|
|
|
free(cgroup_short);
|
2021-10-19 21:36:31 +00:00
|
|
|
} else {
|
2021-12-09 17:14:49 +00:00
|
|
|
//CCGROUP is alias to normal CGROUP if shortening fails
|
|
|
|
Process_updateFieldWidth(CCGROUP, strlen(process->cgroup));
|
2021-10-19 21:36:31 +00:00
|
|
|
free(process->cgroup_short);
|
|
|
|
process->cgroup_short = NULL;
|
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_VSERVER
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static void LinuxProcessList_readVServerData(LinuxProcess* process, openat_arg_t procFd) {
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* file = fopenat(procFd, "status", "r");
|
2014-11-24 20:55:49 +00:00
|
|
|
if (!file)
|
|
|
|
return;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2015-12-14 15:27:11 +00:00
|
|
|
char buffer[PROC_LINE_LENGTH + 1];
|
2014-11-24 20:55:49 +00:00
|
|
|
process->vxid = 0;
|
2015-12-14 15:27:11 +00:00
|
|
|
while (fgets(buffer, PROC_LINE_LENGTH, file)) {
|
2014-11-24 20:55:49 +00:00
|
|
|
if (String_startsWith(buffer, "VxID:")) {
|
|
|
|
int vxid;
|
|
|
|
int ok = sscanf(buffer, "VxID:\t%32d", &vxid);
|
|
|
|
if (ok >= 1) {
|
|
|
|
process->vxid = vxid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if defined HAVE_ANCIENT_VSERVER
|
|
|
|
else if (String_startsWith(buffer, "s_context:")) {
|
|
|
|
int vxid;
|
|
|
|
int ok = sscanf(buffer, "s_context:\t%32d", &vxid);
|
|
|
|
if (ok >= 1) {
|
|
|
|
process->vxid = vxid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t procFd) {
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* file = fopenat(procFd, "oom_score", "r");
|
|
|
|
if (!file)
|
2014-11-24 20:55:49 +00:00
|
|
|
return;
|
2020-11-17 18:46:12 +00:00
|
|
|
|
2015-12-14 15:27:11 +00:00
|
|
|
char buffer[PROC_LINE_LENGTH + 1];
|
|
|
|
if (fgets(buffer, PROC_LINE_LENGTH, file)) {
|
2014-11-24 20:55:49 +00:00
|
|
|
unsigned int oom;
|
2020-08-28 12:24:40 +00:00
|
|
|
int ok = sscanf(buffer, "%u", &oom);
|
2014-11-24 20:55:49 +00:00
|
|
|
if (ok >= 1) {
|
|
|
|
process->oom = oom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
|
2021-08-06 06:45:30 +00:00
|
|
|
static void LinuxProcessList_readAutogroup(LinuxProcess* process, openat_arg_t procFd) {
|
|
|
|
process->autogroup_id = -1;
|
|
|
|
|
|
|
|
char autogroup[64]; // space for two numeric values and fixed length strings
|
|
|
|
ssize_t amtRead = xReadfileat(procFd, "autogroup", autogroup, sizeof(autogroup));
|
|
|
|
if (amtRead < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
long int identity;
|
|
|
|
int nice;
|
|
|
|
int ok = sscanf(autogroup, "/autogroup-%ld nice %d", &identity, &nice);
|
|
|
|
if (ok == 2) {
|
|
|
|
process->autogroup_id = identity;
|
|
|
|
process->autogroup_nice = nice;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) {
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* file = fopenat(procFd, "status", "r");
|
2020-09-11 13:02:00 +00:00
|
|
|
if (!file)
|
|
|
|
return;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2020-09-11 13:02:00 +00:00
|
|
|
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);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (ok >= 1) {
|
2020-09-11 13:02:00 +00:00
|
|
|
ctxt += vctxt;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-11 13:02:00 +00:00
|
|
|
} else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) {
|
|
|
|
unsigned long nvctxt;
|
|
|
|
int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (ok >= 1) {
|
2020-09-11 13:02:00 +00:00
|
|
|
ctxt += nvctxt;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-11 13:02:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
process->ctxt_diff = (ctxt > process->ctxt_total) ? (ctxt - process->ctxt_total) : 0;
|
|
|
|
process->ctxt_total = ctxt;
|
|
|
|
}
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t procFd) {
|
2020-11-17 18:46:12 +00:00
|
|
|
FILE* file = fopenat(procFd, "attr/current", "r");
|
2020-09-28 10:06:13 +00:00
|
|
|
if (!file) {
|
|
|
|
free(process->secattr);
|
|
|
|
process->secattr = NULL;
|
|
|
|
return;
|
|
|
|
}
|
2020-11-17 18:46:12 +00:00
|
|
|
|
2020-09-28 10:06:13 +00:00
|
|
|
char buffer[PROC_LINE_LENGTH + 1];
|
2021-01-05 22:42:55 +00:00
|
|
|
const char* res = fgets(buffer, sizeof(buffer), file);
|
2020-09-28 10:06:13 +00:00
|
|
|
fclose(file);
|
|
|
|
if (!res) {
|
|
|
|
free(process->secattr);
|
|
|
|
process->secattr = NULL;
|
|
|
|
return;
|
|
|
|
}
|
2020-10-31 22:28:02 +00:00
|
|
|
char* newline = strchr(buffer, '\n');
|
2020-11-01 00:09:51 +00:00
|
|
|
if (newline) {
|
2020-09-28 10:06:13 +00:00
|
|
|
*newline = '\0';
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2021-12-04 18:57:47 +00:00
|
|
|
|
|
|
|
Process_updateFieldWidth(SECATTR, strlen(buffer));
|
|
|
|
|
2020-11-01 00:09:51 +00:00
|
|
|
if (process->secattr && String_eq(process->secattr, buffer)) {
|
2020-09-28 10:06:13 +00:00
|
|
|
return;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2021-01-05 13:47:49 +00:00
|
|
|
free_and_xStrdup(&process->secattr, buffer);
|
2020-09-28 10:06:13 +00:00
|
|
|
}
|
|
|
|
|
2020-11-26 16:51:54 +00:00
|
|
|
static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) {
|
2020-12-06 10:51:03 +00:00
|
|
|
char pathBuffer[PATH_MAX + 1] = {0};
|
2020-11-26 16:51:54 +00:00
|
|
|
|
|
|
|
#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
|
2020-12-06 10:51:03 +00:00
|
|
|
|
2020-11-26 16:51:54 +00:00
|
|
|
if (r < 0) {
|
2021-05-25 17:02:12 +00:00
|
|
|
free(process->super.procCwd);
|
|
|
|
process->super.procCwd = NULL;
|
2020-11-26 16:51:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pathBuffer[r] = '\0';
|
|
|
|
|
2021-05-25 17:02:12 +00:00
|
|
|
if (process->super.procCwd && String_eq(process->super.procCwd, pathBuffer))
|
2020-11-26 16:51:54 +00:00
|
|
|
return;
|
|
|
|
|
2021-05-25 17:02:12 +00:00
|
|
|
free_and_xStrdup(&process->super.procCwd, pathBuffer);
|
2020-11-26 16:51:54 +00:00
|
|
|
}
|
|
|
|
|
2017-12-04 02:15:29 +00:00
|
|
|
#ifdef HAVE_DELAYACCT
|
|
|
|
|
2020-10-31 22:28:02 +00:00
|
|
|
static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
|
|
|
|
struct nlmsghdr* nlhdr;
|
|
|
|
struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
|
2021-01-05 22:42:55 +00:00
|
|
|
const struct nlattr* nlattr;
|
2020-10-17 21:28:26 +00:00
|
|
|
struct taskstats stats;
|
2018-02-17 22:52:07 +00:00
|
|
|
int rem;
|
|
|
|
LinuxProcess* lp = (LinuxProcess*) linuxProcess;
|
2017-12-04 02:15:29 +00:00
|
|
|
|
2018-02-17 22:52:07 +00:00
|
|
|
nlhdr = nlmsg_hdr(nlmsg);
|
2017-12-04 02:15:29 +00:00
|
|
|
|
|
|
|
if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
|
|
|
|
return NL_SKIP;
|
2018-02-17 22:52:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
|
2020-10-17 21:28:26 +00:00
|
|
|
memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats));
|
|
|
|
assert(lp->super.pid == (pid_t)stats.ac_pid);
|
|
|
|
|
2020-11-21 23:59:00 +00:00
|
|
|
unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
|
2020-10-31 22:28:02 +00:00
|
|
|
#define BOUNDS(x) (isnan(x) ? 0.0 : ((x) > 100) ? 100.0 : (x))
|
2020-10-31 19:08:44 +00:00
|
|
|
#define DELTAPERC(x,y) BOUNDS((float) ((x) - (y)) / timeDelta * 100)
|
2020-10-17 21:28:26 +00:00
|
|
|
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);
|
2018-02-17 22:52:07 +00:00
|
|
|
#undef DELTAPERC
|
|
|
|
#undef BOUNDS
|
2020-10-31 22:28:02 +00:00
|
|
|
|
2020-10-17 21:28:26 +00:00
|
|
|
lp->swapin_delay_total = stats.swapin_delay_total;
|
|
|
|
lp->blkio_delay_total = stats.blkio_delay_total;
|
|
|
|
lp->cpu_delay_total = stats.cpu_delay_total;
|
2020-10-31 22:28:02 +00:00
|
|
|
lp->delay_read_time = stats.ac_etime * 1000;
|
2018-02-17 22:52:07 +00:00
|
|
|
}
|
|
|
|
return NL_OK;
|
2017-12-04 02:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
|
2020-10-31 22:28:02 +00:00
|
|
|
struct nl_msg* msg;
|
2017-12-04 02:15:29 +00:00
|
|
|
|
2020-12-28 19:51:02 +00:00
|
|
|
if (!this->netlink_socket) {
|
2021-07-14 17:15:09 +00:00
|
|
|
LinuxProcessList_initNetlinkSocket(this);
|
|
|
|
if (!this->netlink_socket) {
|
|
|
|
goto delayacct_failure;
|
|
|
|
}
|
2020-12-28 19:51:02 +00:00
|
|
|
}
|
|
|
|
|
2017-12-04 02:15:29 +00:00
|
|
|
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
|
2020-12-28 19:51:02 +00:00
|
|
|
goto delayacct_failure;
|
2017-12-04 02:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (! (msg = nlmsg_alloc())) {
|
2020-12-28 19:51:02 +00:00
|
|
|
goto delayacct_failure;
|
2017-12-04 02:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nl_send_sync(this->netlink_socket, msg) < 0) {
|
2020-12-28 19:51:02 +00:00
|
|
|
goto delayacct_failure;
|
2017-12-04 02:15:29 +00:00
|
|
|
}
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2017-12-04 02:15:29 +00:00
|
|
|
if (nl_recvmsgs_default(this->netlink_socket) < 0) {
|
2020-12-28 19:51:02 +00:00
|
|
|
goto delayacct_failure;
|
2017-12-04 02:15:29 +00:00
|
|
|
}
|
2020-12-28 19:51:02 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
delayacct_failure:
|
|
|
|
process->swapin_delay_percent = NAN;
|
|
|
|
process->blkio_delay_percent = NAN;
|
|
|
|
process->cpu_delay_percent = NAN;
|
2017-12-04 02:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) {
|
2020-10-31 22:28:02 +00:00
|
|
|
char command[4096 + 1]; // max cmdline length on Linux
|
2020-11-25 11:42:36 +00:00
|
|
|
ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command));
|
2021-11-08 14:42:12 +00:00
|
|
|
if (amtRead <= 0)
|
2020-11-25 11:42:36 +00:00
|
|
|
return false;
|
2020-11-07 21:52:28 +00:00
|
|
|
|
|
|
|
int tokenEnd = 0;
|
|
|
|
int tokenStart = 0;
|
|
|
|
int lastChar = 0;
|
|
|
|
bool argSepNUL = false;
|
|
|
|
bool argSepSpace = false;
|
|
|
|
|
2016-08-24 21:11:10 +00:00
|
|
|
for (int i = 0; i < amtRead; i++) {
|
2020-10-17 10:54:45 +00:00
|
|
|
/* newline used as delimiter - when forming the mergedCommand, newline is
|
2021-04-10 11:31:39 +00:00
|
|
|
* converted to space by Process_makeCommandStr */
|
2020-10-17 10:54:45 +00:00
|
|
|
if (command[i] == '\0') {
|
|
|
|
command[i] = '\n';
|
2020-11-07 21:52:28 +00:00
|
|
|
} else {
|
|
|
|
/* Record some information for the argument parsing heuristic below. */
|
|
|
|
if (tokenEnd)
|
|
|
|
argSepNUL = true;
|
|
|
|
if (command[i] <= ' ')
|
|
|
|
argSepSpace = true;
|
2020-10-17 10:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (command[i] == '\n') {
|
2016-08-24 21:11:10 +00:00
|
|
|
if (tokenEnd == 0) {
|
|
|
|
tokenEnd = i;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2016-08-24 21:11:10 +00:00
|
|
|
} else {
|
2020-10-17 10:54:45 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2016-08-24 21:11:10 +00:00
|
|
|
lastChar = i;
|
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2020-11-07 21:52:28 +00:00
|
|
|
|
|
|
|
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) {
|
2020-12-02 15:06:19 +00:00
|
|
|
if (command[i] == '/' || (command[i] == '\\' && (!tokenStart || command[tokenStart - 1] == '\\'))) {
|
2020-11-07 21:52:28 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-23 13:53:23 +00:00
|
|
|
|
|
|
|
/* Some command lines are hard to parse, like
|
|
|
|
* file.so [kdeinit5] file local:/run/user/1000/klauncherdqbouY.1.slave-socket local:/run/user/1000/kded5TwsDAx.1.slave-socket
|
|
|
|
* Reset if start is behind end.
|
|
|
|
*/
|
|
|
|
if (tokenStart >= tokenEnd)
|
|
|
|
tokenStart = tokenEnd = 0;
|
2020-11-07 21:52:28 +00:00
|
|
|
}
|
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
if (tokenEnd == 0) {
|
2020-11-07 21:52:28 +00:00
|
|
|
tokenEnd = lastChar + 1;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2020-11-07 21:52:28 +00:00
|
|
|
|
2021-05-18 20:36:03 +00:00
|
|
|
Process_updateCmdline(process, command, tokenStart, tokenEnd);
|
2020-10-17 10:54:45 +00:00
|
|
|
|
2020-11-25 11:49:17 +00:00
|
|
|
/* /proc/[pid]/comm could change, so should be updated */
|
2020-11-25 11:42:36 +00:00
|
|
|
if ((amtRead = xReadfileat(procFd, "comm", command, sizeof(command))) > 0) {
|
|
|
|
command[amtRead - 1] = '\0';
|
2021-05-18 20:36:03 +00:00
|
|
|
Process_updateComm(process, command);
|
2021-08-08 14:04:26 +00:00
|
|
|
} else {
|
2021-05-18 20:36:03 +00:00
|
|
|
Process_updateComm(process, NULL);
|
2020-10-17 10:54:45 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 18:46:12 +00:00
|
|
|
char filename[MAX_NAME + 1];
|
|
|
|
|
2020-11-25 11:49:17 +00:00
|
|
|
/* execve could change /proc/[pid]/exe, so procExe should be updated */
|
2020-11-20 16:50:34 +00:00
|
|
|
#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) {
|
2020-10-17 10:54:45 +00:00
|
|
|
filename[amtRead] = 0;
|
2021-02-01 22:23:10 +00:00
|
|
|
if (!process->procExe ||
|
2021-09-24 18:53:34 +00:00
|
|
|
(!process->procExeDeleted && !String_eq(filename, process->procExe)) ||
|
|
|
|
process->procExeDeleted) {
|
2020-11-23 21:55:56 +00:00
|
|
|
|
|
|
|
const char* deletedMarker = " (deleted)";
|
2021-05-18 20:36:03 +00:00
|
|
|
const size_t markerLen = strlen(deletedMarker);
|
|
|
|
const size_t filenameLen = strlen(filename);
|
2020-11-23 21:55:56 +00:00
|
|
|
|
2021-05-18 20:36:03 +00:00
|
|
|
if (filenameLen > markerLen) {
|
2021-05-23 00:48:41 +00:00
|
|
|
bool oldExeDeleted = process->procExeDeleted;
|
|
|
|
|
2021-05-18 20:36:03 +00:00
|
|
|
process->procExeDeleted = String_eq(filename + filenameLen - markerLen, deletedMarker);
|
2021-02-01 22:23:10 +00:00
|
|
|
|
2021-05-18 20:36:03 +00:00
|
|
|
if (process->procExeDeleted)
|
|
|
|
filename[filenameLen - markerLen] = '\0';
|
2021-05-23 00:48:41 +00:00
|
|
|
|
|
|
|
process->mergedCommand.exeChanged |= oldExeDeleted ^ process->procExeDeleted;
|
2020-11-23 21:55:56 +00:00
|
|
|
}
|
2021-05-18 20:36:03 +00:00
|
|
|
|
|
|
|
Process_updateExe(process, filename);
|
2020-10-17 10:54:45 +00:00
|
|
|
}
|
2021-01-30 14:31:59 +00:00
|
|
|
} else if (process->procExe) {
|
2021-05-18 20:36:03 +00:00
|
|
|
Process_updateExe(process, NULL);
|
2021-02-01 21:37:41 +00:00
|
|
|
process->procExeDeleted = false;
|
2020-10-17 10:54:45 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-21 18:40:56 +00:00
|
|
|
static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned long int tty_nr) {
|
2016-10-01 06:09:04 +00:00
|
|
|
unsigned int maj = major(tty_nr);
|
|
|
|
unsigned int min = minor(tty_nr);
|
|
|
|
|
|
|
|
int i = -1;
|
|
|
|
for (;;) {
|
|
|
|
i++;
|
|
|
|
if ((!ttyDrivers[i].path) || maj < ttyDrivers[i].major) {
|
|
|
|
break;
|
2019-10-31 16:39:12 +00:00
|
|
|
}
|
2016-10-01 06:09:04 +00:00
|
|
|
if (maj > ttyDrivers[i].major) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (min < ttyDrivers[i].minorFrom) {
|
|
|
|
break;
|
2019-10-31 16:39:12 +00:00
|
|
|
}
|
2016-10-01 06:09:04 +00:00
|
|
|
if (min > ttyDrivers[i].minorTo) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
unsigned int idx = min - ttyDrivers[i].minorFrom;
|
|
|
|
struct stat sstat;
|
|
|
|
char* fullPath;
|
2020-10-31 19:55:36 +00:00
|
|
|
for (;;) {
|
2020-08-21 08:37:29 +00:00
|
|
|
xAsprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx);
|
2016-10-01 06:09:04 +00:00
|
|
|
int err = stat(fullPath, &sstat);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) {
|
|
|
|
return fullPath;
|
|
|
|
}
|
2016-10-01 06:09:04 +00:00
|
|
|
free(fullPath);
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2020-08-21 08:37:29 +00:00
|
|
|
xAsprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx);
|
2016-10-01 06:09:04 +00:00
|
|
|
err = stat(fullPath, &sstat);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) {
|
|
|
|
return fullPath;
|
|
|
|
}
|
2016-10-01 06:09:04 +00:00
|
|
|
free(fullPath);
|
2020-11-01 00:09:51 +00:00
|
|
|
|
|
|
|
if (idx == min) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-01 06:09:04 +00:00
|
|
|
idx = min;
|
|
|
|
}
|
|
|
|
int err = stat(ttyDrivers[i].path, &sstat);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (err == 0 && tty_nr == sstat.st_rdev) {
|
|
|
|
return xStrdup(ttyDrivers[i].path);
|
|
|
|
}
|
2016-10-01 06:09:04 +00:00
|
|
|
}
|
|
|
|
char* out;
|
2020-08-21 08:37:29 +00:00
|
|
|
xAsprintf(&out, "/dev/%u:%u", maj, min);
|
2016-10-01 06:09:04 +00:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2021-04-09 17:14:24 +00:00
|
|
|
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) {
|
2015-03-16 06:25:43 +00:00
|
|
|
ProcessList* pl = (ProcessList*) this;
|
2020-11-15 19:09:50 +00:00
|
|
|
const struct dirent* entry;
|
2020-10-21 19:26:09 +00:00
|
|
|
const Settings* settings = pl->settings;
|
2021-08-31 05:38:52 +00:00
|
|
|
const ScreenSettings* ss = settings->ss;
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
#ifdef HAVE_OPENAT
|
|
|
|
int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
|
|
|
if (dirFd < 0)
|
|
|
|
return false;
|
2020-11-17 18:46:12 +00:00
|
|
|
DIR* dir = fdopendir(dirFd);
|
2020-11-20 16:50:34 +00:00
|
|
|
#else
|
|
|
|
char dirFd[4096];
|
|
|
|
xSnprintf(dirFd, sizeof(dirFd), "%s/%s", parentFd, dirname);
|
|
|
|
DIR* dir = opendir(dirFd);
|
|
|
|
#endif
|
2020-11-17 18:46:12 +00:00
|
|
|
if (!dir) {
|
2020-11-20 16:50:34 +00:00
|
|
|
Compat_openatArgClose(dirFd);
|
2020-11-01 00:09:51 +00:00
|
|
|
return false;
|
2020-11-17 18:46:12 +00:00
|
|
|
}
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
const unsigned int activeCPUs = pl->activeCPUs;
|
|
|
|
const bool hideKernelThreads = settings->hideKernelThreads;
|
|
|
|
const bool hideUserlandThreads = settings->hideUserlandThreads;
|
2014-11-24 20:55:49 +00:00
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
2020-11-15 19:09:50 +00:00
|
|
|
const char* name = entry->d_name;
|
|
|
|
|
|
|
|
// Ignore all non-directories
|
|
|
|
if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
|
|
|
|
// The RedHat kernel hides threads with a dot.
|
|
|
|
// I believe this is non-standard.
|
2020-11-15 19:07:24 +00:00
|
|
|
if (name[0] == '.') {
|
2014-11-24 20:55:49 +00:00
|
|
|
name++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just skip all non-number directories.
|
|
|
|
if (name[0] < '0' || name[0] > '9') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// filename is a number: process directory
|
2021-04-18 13:49:45 +00:00
|
|
|
int pid;
|
|
|
|
{
|
|
|
|
char* endptr;
|
|
|
|
unsigned long parsedPid = strtoul(name, &endptr, 10);
|
|
|
|
if (parsedPid == 0 || parsedPid == ULONG_MAX || *endptr != '\0')
|
|
|
|
continue;
|
|
|
|
pid = parsedPid;
|
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-04-18 13:49:45 +00:00
|
|
|
// Skip task directory of main thread
|
2020-11-17 18:46:12 +00:00
|
|
|
if (parent && pid == parent->pid)
|
2014-11-24 20:55:49 +00:00
|
|
|
continue;
|
|
|
|
|
2020-11-17 18:46:12 +00:00
|
|
|
bool preExisting;
|
2020-10-21 19:26:05 +00:00
|
|
|
Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcess* lp = (LinuxProcess*) proc;
|
|
|
|
|
2015-03-17 02:01:48 +00:00
|
|
|
proc->tgid = parent ? parent->pid : pid;
|
2021-04-10 12:08:26 +00:00
|
|
|
proc->isUserlandThread = proc->pid != proc->tgid;
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
#ifdef HAVE_OPENAT
|
2021-06-09 08:55:22 +00:00
|
|
|
int procFd = openat(dirFd, entry->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
2020-11-17 18:46:12 +00:00
|
|
|
if (procFd < 0)
|
|
|
|
goto errorReadingProcess;
|
2020-11-20 16:50:34 +00:00
|
|
|
#else
|
|
|
|
char procFd[4096];
|
2020-11-29 11:46:25 +00:00
|
|
|
xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
|
2020-11-20 16:50:34 +00:00
|
|
|
#endif
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-04-09 17:14:24 +00:00
|
|
|
LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period);
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2020-11-19 14:15:02 +00:00
|
|
|
/*
|
2020-11-24 10:46:17 +00:00
|
|
|
* These conditions will not trigger on first occurrence, cause we need to
|
2020-11-19 14:15:02 +00:00
|
|
|
* 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++;
|
2020-11-20 16:50:34 +00:00
|
|
|
Compat_openatArgClose(procFd);
|
2020-11-19 14:15:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
|
|
|
|
proc->updated = true;
|
|
|
|
proc->show = false;
|
|
|
|
pl->userlandThreads++;
|
|
|
|
pl->totalTasks++;
|
2020-11-20 16:50:34 +00:00
|
|
|
Compat_openatArgClose(procFd);
|
2020-11-19 14:15:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_IO)
|
2021-04-09 17:14:24 +00:00
|
|
|
LinuxProcessList_readIoFile(lp, procFd, pl->realtimeMs);
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
if (!LinuxProcessList_readStatmFile(lp, procFd))
|
2014-11-24 20:55:49 +00:00
|
|
|
goto errorReadingProcess;
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
{
|
|
|
|
bool prev = proc->usesDeletedLib;
|
|
|
|
|
2021-12-17 11:29:01 +00:00
|
|
|
if (!proc->isKernelThread && !proc->isUserlandThread &&
|
|
|
|
((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted))) {
|
|
|
|
|
2021-04-04 16:07:26 +00:00
|
|
|
// Check if we really should recalculate the M_LRS value for this process
|
|
|
|
uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime;
|
|
|
|
|
|
|
|
uint64_t recheck = ((uint64_t)rand()) % 2048;
|
|
|
|
|
2021-08-28 15:57:47 +00:00
|
|
|
if (passedTimeInMs > recheck) {
|
2021-04-04 16:07:26 +00:00
|
|
|
lp->last_mlrs_calctime = pl->realtimeMs;
|
2021-08-31 05:38:52 +00:00
|
|
|
LinuxProcessList_readMaps(lp, procFd, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe);
|
2021-04-04 16:07:26 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-12 13:19:22 +00:00
|
|
|
/* Copy from process structure in threads and reset if setting got disabled */
|
|
|
|
proc->usesDeletedLib = (proc->isUserlandThread && parent) ? parent->usesDeletedLib : false;
|
2021-12-17 11:29:01 +00:00
|
|
|
lp->m_lrs = (proc->isUserlandThread && parent) ? ((const LinuxProcess*)parent)->m_lrs : 0;
|
2021-04-04 16:07:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
proc->mergedCommand.exeChanged |= prev ^ proc->usesDeletedLib;
|
|
|
|
}
|
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {
|
2020-10-31 22:28:02 +00:00
|
|
|
if (!parent) {
|
2018-10-09 19:49:29 +00:00
|
|
|
// Read smaps file of each process only every second pass to improve performance
|
|
|
|
static int smaps_flag = 0;
|
2020-10-31 22:28:02 +00:00
|
|
|
if ((pid & 1) == smaps_flag) {
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcessList_readSmapsFile(lp, procFd, this->haveSmapsRollup);
|
2018-10-09 19:49:29 +00:00
|
|
|
}
|
|
|
|
if (pid == 1) {
|
|
|
|
smaps_flag = !smaps_flag;
|
|
|
|
}
|
2020-10-31 20:10:12 +00:00
|
|
|
} else {
|
2020-11-25 21:14:35 +00:00
|
|
|
lp->m_pss = ((const LinuxProcess*)parent)->m_pss;
|
2020-10-31 20:10:12 +00:00
|
|
|
}
|
2018-10-09 19:49:29 +00:00
|
|
|
}
|
|
|
|
|
2021-05-23 13:21:51 +00:00
|
|
|
char statCommand[MAX_NAME + 1];
|
2015-03-16 04:43:04 +00:00
|
|
|
unsigned long long int lasttimes = (lp->utime + lp->stime);
|
2021-03-21 18:40:56 +00:00
|
|
|
unsigned long int tty_nr = proc->tty_nr;
|
2021-05-23 13:21:51 +00:00
|
|
|
if (! LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand)))
|
2014-11-24 20:55:49 +00:00
|
|
|
goto errorReadingProcess;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-11-08 14:42:12 +00:00
|
|
|
if (lp->flags & PF_KTHREAD) {
|
|
|
|
proc->isKernelThread = true;
|
|
|
|
}
|
|
|
|
|
2016-10-01 06:09:04 +00:00
|
|
|
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
|
2021-03-21 18:40:56 +00:00
|
|
|
free(proc->tty_name);
|
|
|
|
proc->tty_name = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
|
2016-10-01 06:09:04 +00:00
|
|
|
}
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) {
|
2015-03-16 04:43:04 +00:00
|
|
|
LinuxProcess_updateIOPriority(lp);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
|
|
|
|
2020-11-29 13:03:48 +00:00
|
|
|
/* period might be 0 after system sleep */
|
2020-12-20 16:17:51 +00:00
|
|
|
float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
|
2021-06-12 16:17:28 +00:00
|
|
|
proc->percent_cpu = CLAMP(percent_cpu, 0.0F, activeCPUs * 100.0F);
|
2020-12-10 00:57:48 +00:00
|
|
|
proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0;
|
2022-02-27 19:29:40 +00:00
|
|
|
Process_updateCPUFieldWidths(proc->percent_cpu);
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-06-09 09:13:39 +00:00
|
|
|
if (! LinuxProcessList_updateUser(pl, proc, procFd))
|
|
|
|
goto errorReadingProcess;
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-06-09 09:13:39 +00:00
|
|
|
if (!preExisting) {
|
2014-11-24 20:55:49 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_OPENVZ
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) {
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcessList_readOpenVZData(lp, procFd);
|
2015-05-13 18:00:58 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
#endif
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
#ifdef HAVE_VSERVER
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) {
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcessList_readVServerData(lp, procFd);
|
2015-05-13 18:00:58 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
#endif
|
|
|
|
|
2021-11-08 14:42:12 +00:00
|
|
|
if (proc->isKernelThread) {
|
|
|
|
Process_updateCmdline(proc, NULL, 0, 0);
|
|
|
|
} else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
|
|
|
|
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
|
2015-05-13 18:00:58 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2020-10-13 12:26:40 +00:00
|
|
|
Process_fillStarttimeBuffer(proc);
|
2020-10-12 10:51:18 +00:00
|
|
|
|
2015-03-17 02:01:48 +00:00
|
|
|
ProcessList_add(pl, proc);
|
2014-11-24 20:55:49 +00:00
|
|
|
} else {
|
2021-10-11 22:45:09 +00:00
|
|
|
if (settings->updateProcessNames && proc->state != ZOMBIE) {
|
2021-11-08 14:42:12 +00:00
|
|
|
if (proc->isKernelThread) {
|
|
|
|
Process_updateCmdline(proc, NULL, 0, 0);
|
|
|
|
} else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
|
|
|
|
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
|
2015-05-13 18:00:58 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 02:15:29 +00:00
|
|
|
#ifdef HAVE_DELAYACCT
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) {
|
2020-12-28 19:51:02 +00:00
|
|
|
LinuxProcessList_readDelayAcctData(this, lp);
|
|
|
|
}
|
2017-12-04 02:15:29 +00:00
|
|
|
#endif
|
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) {
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcessList_readCGroupFile(lp, procFd);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_OOM) {
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcessList_readOomData(lp, procFd);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_CTXT) {
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcessList_readCtxtData(lp, procFd);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-11 13:02:00 +00:00
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) {
|
2020-11-17 18:46:12 +00:00
|
|
|
LinuxProcessList_readSecattrData(lp, procFd);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-28 10:06:13 +00:00
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->flags & PROCESS_FLAG_CWD) {
|
2020-11-26 16:51:54 +00:00
|
|
|
LinuxProcessList_readCwd(lp, procFd);
|
|
|
|
}
|
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if ((ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) {
|
2021-08-06 06:45:30 +00:00
|
|
|
LinuxProcessList_readAutogroup(lp, procFd);
|
|
|
|
}
|
|
|
|
|
2021-08-08 14:04:26 +00:00
|
|
|
if (!proc->cmdline && statCommand[0] &&
|
2021-10-11 22:45:09 +00:00
|
|
|
(proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) {
|
2021-05-23 13:21:51 +00:00
|
|
|
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
|
2021-08-08 14:04:26 +00:00
|
|
|
}
|
2021-01-05 13:59:37 +00:00
|
|
|
|
2021-08-08 14:04:26 +00:00
|
|
|
if (Process_isKernelThread(proc)) {
|
|
|
|
pl->kernelThreads++;
|
|
|
|
} else if (Process_isUserlandThread(proc)) {
|
|
|
|
pl->userlandThreads++;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
|
2020-11-23 13:44:31 +00:00
|
|
|
/* Set at the end when we know if a new entry is a thread */
|
|
|
|
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
|
|
|
|
|
2015-03-17 02:03:40 +00:00
|
|
|
pl->totalTasks++;
|
2021-02-17 15:26:10 +00:00
|
|
|
/* runningTasks is set in LinuxProcessList_scanCPUTime() from /proc/stat */
|
2015-03-16 04:43:04 +00:00
|
|
|
proc->updated = true;
|
2020-11-20 16:50:34 +00:00
|
|
|
Compat_openatArgClose(procFd);
|
2014-11-24 20:55:49 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Exception handler.
|
2020-10-31 21:14:27 +00:00
|
|
|
|
|
|
|
errorReadingProcess:
|
|
|
|
{
|
2020-11-20 16:50:34 +00:00
|
|
|
#ifdef HAVE_OPENAT
|
2020-11-17 18:46:12 +00:00
|
|
|
if (procFd >= 0)
|
|
|
|
close(procFd);
|
2020-11-20 16:50:34 +00:00
|
|
|
#endif
|
2020-11-17 18:46:12 +00:00
|
|
|
|
2015-03-17 02:01:48 +00:00
|
|
|
if (preExisting) {
|
2015-03-17 02:03:40 +00:00
|
|
|
ProcessList_remove(pl, proc);
|
2015-03-17 02:01:48 +00:00
|
|
|
} else {
|
2015-03-16 04:43:04 +00:00
|
|
|
Process_delete((Object*)proc);
|
2015-03-17 02:01:48 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-16 06:25:43 +00:00
|
|
|
static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
|
2021-01-06 17:11:24 +00:00
|
|
|
memory_t availableMem = 0;
|
|
|
|
memory_t freeMem = 0;
|
|
|
|
memory_t totalMem = 0;
|
|
|
|
memory_t buffersMem = 0;
|
|
|
|
memory_t cachedMem = 0;
|
2021-03-03 18:48:30 +00:00
|
|
|
memory_t sharedMem = 0;
|
2021-01-06 17:11:24 +00:00
|
|
|
memory_t swapTotalMem = 0;
|
|
|
|
memory_t swapCacheMem = 0;
|
|
|
|
memory_t swapFreeMem = 0;
|
|
|
|
memory_t sreclaimableMem = 0;
|
2014-11-27 18:31:42 +00:00
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
FILE* file = fopen(PROCMEMINFOFILE, "r");
|
2021-01-06 17:11:24 +00:00
|
|
|
if (!file)
|
2014-11-24 20:55:49 +00:00
|
|
|
CRT_fatalError("Cannot open " PROCMEMINFOFILE);
|
2021-01-06 17:11:24 +00:00
|
|
|
|
2014-11-27 18:31:42 +00:00
|
|
|
char buffer[128];
|
2021-01-06 17:11:24 +00:00
|
|
|
while (fgets(buffer, sizeof(buffer), file)) {
|
2014-11-27 18:31:42 +00:00
|
|
|
|
2021-01-06 17:11:24 +00:00
|
|
|
#define tryRead(label, variable) \
|
|
|
|
if (String_startsWith(buffer, label)) { \
|
|
|
|
memory_t parsed_; \
|
|
|
|
if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \
|
2021-03-02 21:00:44 +00:00
|
|
|
(variable) = parsed_; \
|
2021-01-06 17:11:24 +00:00
|
|
|
} \
|
|
|
|
break; \
|
2021-08-14 09:21:54 +00:00
|
|
|
} else (void) 0 /* Require a ";" after the macro use. */
|
2020-10-31 19:18:40 +00:00
|
|
|
|
2014-11-27 18:31:42 +00:00
|
|
|
switch (buffer[0]) {
|
|
|
|
case 'M':
|
2021-01-06 17:11:24 +00:00
|
|
|
tryRead("MemAvailable:", availableMem);
|
|
|
|
tryRead("MemFree:", freeMem);
|
|
|
|
tryRead("MemTotal:", totalMem);
|
2014-11-27 18:31:42 +00:00
|
|
|
break;
|
|
|
|
case 'B':
|
2021-01-06 17:11:24 +00:00
|
|
|
tryRead("Buffers:", buffersMem);
|
2014-11-27 18:31:42 +00:00
|
|
|
break;
|
|
|
|
case 'C':
|
2021-01-06 17:11:24 +00:00
|
|
|
tryRead("Cached:", cachedMem);
|
2014-11-27 18:31:42 +00:00
|
|
|
break;
|
|
|
|
case 'S':
|
2015-11-29 00:22:00 +00:00
|
|
|
switch (buffer[1]) {
|
2021-03-03 18:48:30 +00:00
|
|
|
case 'h':
|
|
|
|
tryRead("Shmem:", sharedMem);
|
|
|
|
break;
|
2015-11-29 00:22:00 +00:00
|
|
|
case 'w':
|
2021-01-06 17:11:24 +00:00
|
|
|
tryRead("SwapTotal:", swapTotalMem);
|
|
|
|
tryRead("SwapCached:", swapCacheMem);
|
|
|
|
tryRead("SwapFree:", swapFreeMem);
|
2015-11-29 00:22:00 +00:00
|
|
|
break;
|
|
|
|
case 'R':
|
2021-01-06 17:11:24 +00:00
|
|
|
tryRead("SReclaimable:", sreclaimableMem);
|
2015-11-29 00:22:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-11-27 18:31:42 +00:00
|
|
|
break;
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2021-01-06 17:11:24 +00:00
|
|
|
|
2015-11-29 00:22:00 +00:00
|
|
|
#undef tryRead
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
2021-01-06 17:11:24 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute memory partition like procps(free)
|
|
|
|
* https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c
|
2021-04-14 16:26:38 +00:00
|
|
|
*
|
|
|
|
* Adjustments:
|
|
|
|
* - Shmem in part of Cached (see https://lore.kernel.org/patchwork/patch/648763/),
|
|
|
|
* do not show twice by subtracting from Cached and do not subtract twice from used.
|
2021-01-06 17:11:24 +00:00
|
|
|
*/
|
|
|
|
this->totalMem = totalMem;
|
2021-04-14 16:26:38 +00:00
|
|
|
this->cachedMem = cachedMem + sreclaimableMem - sharedMem;
|
2021-03-03 18:48:30 +00:00
|
|
|
this->sharedMem = sharedMem;
|
2021-04-14 16:26:38 +00:00
|
|
|
const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem;
|
2021-01-06 17:11:24 +00:00
|
|
|
this->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem;
|
|
|
|
this->buffersMem = buffersMem;
|
|
|
|
this->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem;
|
|
|
|
this->totalSwap = swapTotalMem;
|
|
|
|
this->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem;
|
|
|
|
this->cachedSwap = swapCacheMem;
|
2014-11-27 18:28:32 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-01-05 14:50:23 +00:00
|
|
|
static void LinuxProcessList_scanHugePages(LinuxProcessList* this) {
|
|
|
|
this->totalHugePageMem = 0;
|
2021-01-14 14:46:37 +00:00
|
|
|
for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) {
|
2021-01-06 17:11:24 +00:00
|
|
|
this->usedHugePageMem[i] = MEMORY_MAX;
|
2021-01-14 14:46:37 +00:00
|
|
|
}
|
2021-01-05 14:50:23 +00:00
|
|
|
|
|
|
|
DIR* dir = opendir("/sys/kernel/mm/hugepages");
|
|
|
|
if (!dir)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const struct dirent* entry;
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
const char* name = entry->d_name;
|
|
|
|
|
|
|
|
/* Ignore all non-directories */
|
|
|
|
if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!String_startsWith(name, "hugepages-"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
char* endptr;
|
|
|
|
unsigned long int hugePageSize = strtoul(name + strlen("hugepages-"), &endptr, 10);
|
|
|
|
if (!endptr || *endptr != 'k')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
char content[64];
|
|
|
|
char hugePagePath[128];
|
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/nr_hugepages", name);
|
|
|
|
r = xReadfile(hugePagePath, content, sizeof(content));
|
|
|
|
if (r <= 0)
|
|
|
|
continue;
|
|
|
|
|
2021-01-06 17:11:24 +00:00
|
|
|
memory_t total = strtoull(content, NULL, 10);
|
2021-01-05 14:50:23 +00:00
|
|
|
if (total == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/free_hugepages", name);
|
|
|
|
r = xReadfile(hugePagePath, content, sizeof(content));
|
|
|
|
if (r <= 0)
|
|
|
|
continue;
|
|
|
|
|
2021-01-06 17:11:24 +00:00
|
|
|
memory_t free = strtoull(content, NULL, 10);
|
2021-01-05 14:50:23 +00:00
|
|
|
|
2021-01-14 14:46:37 +00:00
|
|
|
int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10);
|
|
|
|
assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT);
|
|
|
|
|
2021-01-05 14:50:23 +00:00
|
|
|
this->totalHugePageMem += total * hugePageSize;
|
2021-01-14 14:46:37 +00:00
|
|
|
this->usedHugePageMem[shift] = (total - free) * hugePageSize;
|
2021-01-05 14:50:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
|
2020-09-22 11:54:15 +00:00
|
|
|
static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) {
|
2021-01-06 17:11:24 +00:00
|
|
|
memory_t totalZram = 0;
|
|
|
|
memory_t usedZramComp = 0;
|
|
|
|
memory_t usedZramOrig = 0;
|
2020-09-22 11:54:15 +00:00
|
|
|
|
|
|
|
char mm_stat[34];
|
|
|
|
char disksize[34];
|
|
|
|
|
|
|
|
unsigned int i = 0;
|
2020-10-31 19:55:36 +00:00
|
|
|
for (;;) {
|
2020-09-22 11:54:15 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-01-06 17:11:24 +00:00
|
|
|
memory_t size = 0;
|
|
|
|
memory_t orig_data_size = 0;
|
|
|
|
memory_t compr_data_size = 0;
|
2020-09-22 11:54:15 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-07-07 02:37:02 +00:00
|
|
|
static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
|
2021-01-06 17:11:24 +00:00
|
|
|
memory_t dbufSize = 0;
|
|
|
|
memory_t dnodeSize = 0;
|
|
|
|
memory_t bonusSize = 0;
|
2019-07-07 02:37:02 +00:00
|
|
|
|
|
|
|
FILE* file = fopen(PROCARCSTATSFILE, "r");
|
|
|
|
if (file == NULL) {
|
2019-07-07 23:27:00 +00:00
|
|
|
lpl->zfs.enabled = 0;
|
2019-07-07 02:37:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
char buffer[128];
|
|
|
|
while (fgets(buffer, 128, file)) {
|
2020-10-31 19:18:40 +00:00
|
|
|
#define tryRead(label, variable) \
|
|
|
|
if (String_startsWith(buffer, label)) { \
|
|
|
|
sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
|
|
|
|
break; \
|
2021-08-14 09:21:54 +00:00
|
|
|
} else (void) 0 /* Require a ";" after the macro use. */
|
2020-10-31 19:18:40 +00:00
|
|
|
#define tryReadFlag(label, variable, flag) \
|
|
|
|
if (String_startsWith(buffer, label)) { \
|
|
|
|
(flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
|
|
|
|
break; \
|
2021-08-14 09:21:54 +00:00
|
|
|
} else (void) 0 /* Require a ";" after the macro use. */
|
2020-10-31 19:18:40 +00:00
|
|
|
|
2019-07-07 02:37:02 +00:00
|
|
|
switch (buffer[0]) {
|
|
|
|
case 'c':
|
2019-07-07 23:27:00 +00:00
|
|
|
tryRead("c_max", &lpl->zfs.max);
|
2019-09-03 19:56:38 +00:00
|
|
|
tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed);
|
2019-09-03 18:26:02 +00:00
|
|
|
break;
|
|
|
|
case 'u':
|
2019-09-03 19:56:38 +00:00
|
|
|
tryRead("uncompressed_size", &lpl->zfs.uncompressed);
|
2019-07-07 02:37:02 +00:00
|
|
|
break;
|
|
|
|
case 's':
|
2019-07-07 23:27:00 +00:00
|
|
|
tryRead("size", &lpl->zfs.size);
|
2019-07-07 02:37:02 +00:00
|
|
|
break;
|
|
|
|
case 'h':
|
2019-07-07 23:27:00 +00:00
|
|
|
tryRead("hdr_size", &lpl->zfs.header);
|
2019-07-07 02:37:02 +00:00
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
tryRead("dbuf_size", &dbufSize);
|
|
|
|
tryRead("dnode_size", &dnodeSize);
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
tryRead("bonus_size", &bonusSize);
|
|
|
|
break;
|
|
|
|
case 'a':
|
2019-07-07 23:27:00 +00:00
|
|
|
tryRead("anon_size", &lpl->zfs.anon);
|
2019-07-07 02:37:02 +00:00
|
|
|
break;
|
|
|
|
case 'm':
|
2019-07-07 23:27:00 +00:00
|
|
|
tryRead("mfu_size", &lpl->zfs.MFU);
|
|
|
|
tryRead("mru_size", &lpl->zfs.MRU);
|
2019-07-07 02:37:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#undef tryRead
|
2019-09-03 18:26:02 +00:00
|
|
|
#undef tryReadFlag
|
2019-07-07 02:37:02 +00:00
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
|
2019-07-07 23:27:00 +00:00
|
|
|
lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0);
|
|
|
|
lpl->zfs.size /= 1024;
|
|
|
|
lpl->zfs.max /= 1024;
|
|
|
|
lpl->zfs.MFU /= 1024;
|
|
|
|
lpl->zfs.MRU /= 1024;
|
|
|
|
lpl->zfs.anon /= 1024;
|
|
|
|
lpl->zfs.header /= 1024;
|
|
|
|
lpl->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024;
|
2019-09-03 18:26:02 +00:00
|
|
|
if ( lpl->zfs.isCompressed ) {
|
|
|
|
lpl->zfs.compressed /= 1024;
|
|
|
|
lpl->zfs.uncompressed /= 1024;
|
|
|
|
}
|
2019-07-07 02:37:02 +00:00
|
|
|
}
|
|
|
|
|
2021-02-17 15:26:10 +00:00
|
|
|
static inline double LinuxProcessList_scanCPUTime(ProcessList* super) {
|
|
|
|
LinuxProcessList* this = (LinuxProcessList*) super;
|
2014-11-27 18:31:42 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
LinuxProcessList_updateCPUcount(super);
|
|
|
|
|
2014-11-27 18:28:32 +00:00
|
|
|
FILE* file = fopen(PROCSTATFILE, "r");
|
2021-02-17 15:26:10 +00:00
|
|
|
if (!file)
|
2014-11-24 20:55:49 +00:00
|
|
|
CRT_fatalError("Cannot open " PROCSTATFILE);
|
2021-02-17 15:26:10 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
unsigned int existingCPUs = super->existingCPUs;
|
|
|
|
unsigned int lastAdjCpuId = 0;
|
2021-02-17 15:26:10 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
for (unsigned int i = 0; i <= existingCPUs; i++) {
|
2015-12-14 15:27:11 +00:00
|
|
|
char buffer[PROC_LINE_LENGTH + 1];
|
2015-08-20 04:12:34 +00:00
|
|
|
unsigned long long int usertime, nicetime, systemtime, idletime;
|
2021-02-17 15:26:10 +00:00
|
|
|
unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0;
|
2021-06-12 16:17:28 +00:00
|
|
|
|
2021-02-17 15:26:10 +00:00
|
|
|
const char* ok = fgets(buffer, sizeof(buffer), file);
|
|
|
|
if (!ok)
|
|
|
|
break;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
// cpu fields are sorted first
|
|
|
|
if (!String_startsWith(buffer, "cpu"))
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Depending on your kernel version,
|
|
|
|
// 5, 7, 8 or 9 of these fields will be set.
|
|
|
|
// The rest will remain at zero.
|
|
|
|
unsigned int adjCpuId;
|
2020-11-01 00:09:51 +00:00
|
|
|
if (i == 0) {
|
2020-08-28 13:28:50 +00:00
|
|
|
(void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
|
2021-06-12 16:17:28 +00:00
|
|
|
adjCpuId = 0;
|
2020-11-01 00:09:51 +00:00
|
|
|
} else {
|
2021-02-17 16:38:35 +00:00
|
|
|
unsigned int cpuid;
|
|
|
|
(void) sscanf(buffer, "cpu%4u %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
|
2021-06-12 16:17:28 +00:00
|
|
|
adjCpuId = cpuid + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adjCpuId > super->existingCPUs)
|
|
|
|
break;
|
|
|
|
|
|
|
|
for (unsigned int j = lastAdjCpuId + 1; j < adjCpuId; j++) {
|
|
|
|
// Skipped an ID, but /proc/stat is ordered => got offline CPU
|
|
|
|
memset(&(this->cpuData[j]), '\0', sizeof(CPUData));
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2021-06-12 16:17:28 +00:00
|
|
|
lastAdjCpuId = adjCpuId;
|
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
// Guest time is already accounted in usertime
|
2021-02-17 15:26:10 +00:00
|
|
|
usertime -= guest;
|
|
|
|
nicetime -= guestnice;
|
2014-11-24 20:55:49 +00:00
|
|
|
// Fields existing on kernels >= 2.6
|
|
|
|
// (and RHEL's patched kernel 2.4...)
|
2015-08-20 04:12:34 +00:00
|
|
|
unsigned long long int idlealltime = idletime + ioWait;
|
|
|
|
unsigned long long int systemalltime = systemtime + irq + softIrq;
|
|
|
|
unsigned long long int virtalltime = guest + guestnice;
|
|
|
|
unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
|
2021-06-12 16:17:28 +00:00
|
|
|
CPUData* cpuData = &(this->cpuData[adjCpuId]);
|
2015-08-30 19:26:47 +00:00
|
|
|
// 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.
|
2021-06-12 08:52:57 +00:00
|
|
|
cpuData->userPeriod = saturatingSub(usertime, cpuData->userTime);
|
|
|
|
cpuData->nicePeriod = saturatingSub(nicetime, cpuData->niceTime);
|
|
|
|
cpuData->systemPeriod = saturatingSub(systemtime, cpuData->systemTime);
|
|
|
|
cpuData->systemAllPeriod = saturatingSub(systemalltime, cpuData->systemAllTime);
|
|
|
|
cpuData->idleAllPeriod = saturatingSub(idlealltime, cpuData->idleAllTime);
|
|
|
|
cpuData->idlePeriod = saturatingSub(idletime, cpuData->idleTime);
|
|
|
|
cpuData->ioWaitPeriod = saturatingSub(ioWait, cpuData->ioWaitTime);
|
|
|
|
cpuData->irqPeriod = saturatingSub(irq, cpuData->irqTime);
|
|
|
|
cpuData->softIrqPeriod = saturatingSub(softIrq, cpuData->softIrqTime);
|
|
|
|
cpuData->stealPeriod = saturatingSub(steal, cpuData->stealTime);
|
|
|
|
cpuData->guestPeriod = saturatingSub(virtalltime, cpuData->guestTime);
|
|
|
|
cpuData->totalPeriod = saturatingSub(totaltime, cpuData->totalTime);
|
2014-11-24 20:55:49 +00:00
|
|
|
cpuData->userTime = usertime;
|
|
|
|
cpuData->niceTime = nicetime;
|
|
|
|
cpuData->systemTime = systemtime;
|
|
|
|
cpuData->systemAllTime = systemalltime;
|
|
|
|
cpuData->idleAllTime = idlealltime;
|
|
|
|
cpuData->idleTime = idletime;
|
|
|
|
cpuData->ioWaitTime = ioWait;
|
|
|
|
cpuData->irqTime = irq;
|
|
|
|
cpuData->softIrqTime = softIrq;
|
|
|
|
cpuData->stealTime = steal;
|
|
|
|
cpuData->guestTime = virtalltime;
|
|
|
|
cpuData->totalTime = totaltime;
|
|
|
|
}
|
2020-10-31 20:10:12 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
double period = (double)this->cpuData[0].totalPeriod / super->activeCPUs;
|
2021-02-17 15:26:10 +00:00
|
|
|
|
|
|
|
char buffer[PROC_LINE_LENGTH + 1];
|
|
|
|
while (fgets(buffer, sizeof(buffer), file)) {
|
|
|
|
if (String_startsWith(buffer, "procs_running")) {
|
|
|
|
super->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-30 19:26:47 +00:00
|
|
|
fclose(file);
|
2021-02-17 15:26:10 +00:00
|
|
|
|
2014-11-27 18:28:32 +00:00
|
|
|
return period;
|
|
|
|
}
|
|
|
|
|
2022-04-30 13:01:55 +00:00
|
|
|
static int scanCPUFrequencyFromSysCPUFreq(LinuxProcessList* this) {
|
2021-06-12 16:17:28 +00:00
|
|
|
unsigned int existingCPUs = this->super.existingCPUs;
|
2020-09-22 12:50:50 +00:00
|
|
|
int numCPUsWithFrequency = 0;
|
|
|
|
unsigned long totalFrequency = 0;
|
|
|
|
|
2021-01-13 14:44:05 +00:00
|
|
|
/*
|
|
|
|
* On some AMD and Intel CPUs read()ing scaling_cur_freq is quite slow (> 1ms). This delay
|
|
|
|
* accumulates for every core. For details see issue#471.
|
|
|
|
* If the read on CPU 0 takes longer than 500us bail out and fall back to reading the
|
|
|
|
* frequencies from /proc/cpuinfo.
|
|
|
|
* Once the condition has been met, bail out early for the next couple of scans.
|
|
|
|
*/
|
|
|
|
static int timeout = 0;
|
|
|
|
|
|
|
|
if (timeout > 0) {
|
|
|
|
timeout--;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
for (unsigned int i = 0; i < existingCPUs; ++i) {
|
2021-08-21 21:53:17 +00:00
|
|
|
if (!ProcessList_isCPUonline(&this->super, i))
|
|
|
|
continue;
|
|
|
|
|
2020-09-22 12:50:50 +00:00
|
|
|
char pathBuffer[64];
|
2021-02-17 16:38:35 +00:00
|
|
|
xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
|
2020-09-22 12:50:50 +00:00
|
|
|
|
2021-01-13 14:44:05 +00:00
|
|
|
struct timespec start;
|
|
|
|
if (i == 0)
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
|
|
|
|
2020-09-22 12:50:50 +00:00
|
|
|
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;
|
2021-06-12 16:17:28 +00:00
|
|
|
this->cpuData[i + 1].frequency = frequency;
|
2020-09-22 12:50:50 +00:00
|
|
|
numCPUsWithFrequency++;
|
|
|
|
totalFrequency += frequency;
|
|
|
|
}
|
2019-08-11 05:19:32 +00:00
|
|
|
|
2020-09-22 12:50:50 +00:00
|
|
|
fclose(file);
|
2021-01-13 14:44:05 +00:00
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
struct timespec end;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
|
|
|
const time_t timeTakenUs = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
|
|
|
|
if (timeTakenUs > 500) {
|
|
|
|
timeout = 30;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-11 05:19:32 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 12:50:50 +00:00
|
|
|
if (numCPUsWithFrequency > 0)
|
2021-06-12 16:17:28 +00:00
|
|
|
this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
|
2020-09-22 12:50:50 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-30 13:01:55 +00:00
|
|
|
static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) {
|
2020-09-22 12:50:50 +00:00
|
|
|
FILE* file = fopen(PROCCPUINFOFILE, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
return;
|
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
unsigned int existingCPUs = this->super.existingCPUs;
|
2019-08-11 05:19:32 +00:00
|
|
|
int numCPUsWithFrequency = 0;
|
|
|
|
double totalFrequency = 0;
|
2020-09-22 12:50:50 +00:00
|
|
|
int cpuid = -1;
|
2019-08-11 05:19:32 +00:00
|
|
|
|
2020-09-22 12:50:50 +00:00
|
|
|
while (!feof(file)) {
|
2019-08-11 05:19:32 +00:00
|
|
|
double frequency;
|
2020-09-22 12:50:50 +00:00
|
|
|
char buffer[PROC_LINE_LENGTH];
|
2019-08-11 05:19:32 +00:00
|
|
|
|
2020-09-22 12:50:50 +00:00
|
|
|
if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
|
|
|
|
break;
|
2019-08-11 05:19:32 +00:00
|
|
|
|
2020-09-22 12:50:50 +00:00
|
|
|
if (
|
|
|
|
(sscanf(buffer, "processor : %d", &cpuid) == 1) ||
|
|
|
|
(sscanf(buffer, "processor: %d", &cpuid) == 1)
|
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
} else if (
|
|
|
|
(sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
|
2020-12-23 16:46:32 +00:00
|
|
|
(sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) ||
|
|
|
|
(sscanf(buffer, "clock : %lfMHz", &frequency) == 1) ||
|
|
|
|
(sscanf(buffer, "clock: %lfMHz", &frequency) == 1)
|
2020-09-22 12:50:50 +00:00
|
|
|
) {
|
2021-06-12 16:17:28 +00:00
|
|
|
if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) {
|
2020-09-22 12:50:50 +00:00
|
|
|
continue;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-22 12:50:50 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
CPUData* cpuData = &(this->cpuData[cpuid + 1]);
|
2020-09-22 12:50:50 +00:00
|
|
|
/* do not override sysfs data */
|
2020-11-01 00:09:51 +00:00
|
|
|
if (isnan(cpuData->frequency)) {
|
2020-09-22 12:50:50 +00:00
|
|
|
cpuData->frequency = frequency;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-22 12:50:50 +00:00
|
|
|
numCPUsWithFrequency++;
|
|
|
|
totalFrequency += frequency;
|
|
|
|
} else if (buffer[0] == '\n') {
|
|
|
|
cpuid = -1;
|
2019-08-11 05:19:32 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-22 12:50:50 +00:00
|
|
|
fclose(file);
|
2019-08-11 05:19:32 +00:00
|
|
|
|
2020-11-01 00:09:51 +00:00
|
|
|
if (numCPUsWithFrequency > 0) {
|
2021-06-12 16:17:28 +00:00
|
|
|
this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-22 12:50:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
|
2021-06-12 16:17:28 +00:00
|
|
|
unsigned int existingCPUs = this->super.existingCPUs;
|
2020-09-22 12:50:50 +00:00
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
for (unsigned int i = 0; i <= existingCPUs; i++) {
|
|
|
|
this->cpuData[i].frequency = NAN;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-22 12:50:50 +00:00
|
|
|
|
2022-04-30 13:01:55 +00:00
|
|
|
if (scanCPUFrequencyFromSysCPUFreq(this) == 0) {
|
2020-09-22 12:50:50 +00:00
|
|
|
return;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-09-22 12:50:50 +00:00
|
|
|
|
2022-04-30 13:01:55 +00:00
|
|
|
scanCPUFrequencyFromCPUinfo(this);
|
2019-08-11 05:19:32 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 14:03:37 +00:00
|
|
|
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
2015-01-22 01:27:31 +00:00
|
|
|
LinuxProcessList* this = (LinuxProcessList*) super;
|
2020-09-22 12:50:50 +00:00
|
|
|
const Settings* settings = super->settings;
|
2014-11-27 18:28:32 +00:00
|
|
|
|
2015-03-16 06:25:43 +00:00
|
|
|
LinuxProcessList_scanMemoryInfo(super);
|
2021-01-05 14:50:23 +00:00
|
|
|
LinuxProcessList_scanHugePages(this);
|
2019-07-07 02:37:02 +00:00
|
|
|
LinuxProcessList_scanZfsArcstats(this);
|
2020-09-22 11:54:15 +00:00
|
|
|
LinuxProcessList_scanZramInfo(this);
|
2020-09-22 18:04:41 +00:00
|
|
|
|
2021-02-17 15:26:10 +00:00
|
|
|
double period = LinuxProcessList_scanCPUTime(super);
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2020-11-01 00:09:51 +00:00
|
|
|
if (settings->showCPUFrequency) {
|
2020-09-22 12:50:50 +00:00
|
|
|
LinuxProcessList_scanCPUFrequency(this);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2019-08-11 05:19:32 +00:00
|
|
|
|
2020-12-01 12:59:19 +00:00
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H
|
2020-09-10 17:56:33 +00:00
|
|
|
if (settings->showCPUTemperature)
|
2021-06-12 16:17:28 +00:00
|
|
|
LibSensors_getCPUTemperatures(this->cpuData, this->super.existingCPUs, this->super.activeCPUs);
|
2020-09-10 17:56:33 +00:00
|
|
|
#endif
|
|
|
|
|
2020-10-13 14:03:37 +00:00
|
|
|
// in pause mode only gather global data for meters (CPU/memory/...)
|
2020-11-01 00:09:51 +00:00
|
|
|
if (pauseProcessUpdate) {
|
2020-10-13 14:03:37 +00:00
|
|
|
return;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-10-13 14:03:37 +00:00
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) {
|
2021-08-06 06:45:30 +00:00
|
|
|
// Refer to sched(7) 'autogroup feature' section
|
|
|
|
// The kernel feature can be enabled/disabled through procfs at
|
|
|
|
// any time, so check for it at the start of each sample - only
|
|
|
|
// read from per-process procfs files if it's globally enabled.
|
|
|
|
this->haveAutogroup = LinuxProcess_isAutogroupEnabled();
|
|
|
|
} else {
|
|
|
|
this->haveAutogroup = false;
|
|
|
|
}
|
|
|
|
|
2020-11-20 16:50:34 +00:00
|
|
|
/* PROCDIR is an absolute path */
|
|
|
|
assert(PROCDIR[0] == '/');
|
|
|
|
#ifdef HAVE_OPENAT
|
|
|
|
openat_arg_t rootFd = AT_FDCWD;
|
|
|
|
#else
|
|
|
|
openat_arg_t rootFd = "";
|
|
|
|
#endif
|
2020-11-17 18:46:12 +00:00
|
|
|
|
2021-04-09 17:14:24 +00:00
|
|
|
LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period);
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2021-06-12 20:04:37 +00:00
|
|
|
|
|
|
|
bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) {
|
|
|
|
assert(id < super->existingCPUs);
|
|
|
|
|
|
|
|
const LinuxProcessList* this = (const LinuxProcessList*) super;
|
|
|
|
return this->cpuData[id + 1].online;
|
|
|
|
}
|