aboutsummaryrefslogtreecommitdiffstats
path: root/linux
diff options
context:
space:
mode:
authorDaniel Lange <DLange@git.local>2024-01-10 11:17:08 +0100
committerDaniel Lange <DLange@git.local>2024-01-10 11:17:08 +0100
commite7372d18a1a661d8c3dba9f51e1f17b5f94171a7 (patch)
treee8270dd60ec096bee8157dbadf029e15ed584592 /linux
parent937052b231259a47d881d539ad5748245ef55b99 (diff)
downloaddebian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.tar.gz
debian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.tar.bz2
debian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.zip
New upstream version 3.3.0
Diffstat (limited to 'linux')
-rw-r--r--linux/CGroupUtils.c255
-rw-r--r--linux/CGroupUtils.h6
-rw-r--r--linux/HugePageMeter.c14
-rw-r--r--linux/IOPriorityPanel.c2
-rw-r--r--linux/LibSensors.c41
-rw-r--r--linux/LibSensors.h8
-rw-r--r--linux/LinuxMachine.c695
-rw-r--r--linux/LinuxMachine.h (renamed from linux/LinuxProcessList.h)53
-rw-r--r--linux/LinuxProcess.c204
-rw-r--r--linux/LinuxProcess.h19
-rw-r--r--linux/LinuxProcessTable.c (renamed from linux/LinuxProcessList.c)1369
-rw-r--r--linux/LinuxProcessTable.h35
-rw-r--r--linux/Platform.c321
-rw-r--r--linux/Platform.h39
-rw-r--r--linux/PressureStallMeter.c21
-rw-r--r--linux/PressureStallMeter.h2
-rw-r--r--linux/ProcessField.h2
-rw-r--r--linux/SELinuxMeter.c2
-rw-r--r--linux/SystemdMeter.c189
-rw-r--r--linux/SystemdMeter.h2
-rw-r--r--linux/ZramMeter.c31
-rw-r--r--linux/ZramMeter.h12
-rw-r--r--linux/ZramStats.h8
-rw-r--r--linux/ZswapStats.h19
24 files changed, 1952 insertions, 1397 deletions
diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c
index 22cce91..f352b8e 100644
--- a/linux/CGroupUtils.c
+++ b/linux/CGroupUtils.c
@@ -1,17 +1,48 @@
/*
-htop - CGroupUtils.h
+htop - CGroupUtils.c
(C) 2021 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "linux/CGroupUtils.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Macros.h"
#include "XUtils.h"
+static const char* str_slice_suffix = ".slice";
+static const char* str_system_slice = "system.slice";
+static const char* str_user_slice = "user.slice";
+static const char* str_machine_slice = "machine.slice";
+static const char* str_user_slice_prefix = "/user-";
+static const char* str_system_slice_prefix = "/system-";
+
+static const char* str_lxc_monitor_legacy = "lxc.monitor";
+static const char* str_lxc_payload_legacy = "lxc.payload";
+static const char* str_lxc_monitor_prefix = "lxc.monitor.";
+static const char* str_lxc_payload_prefix = "lxc.payload.";
+
+static const char* str_nspawn_scope_prefix = "machine-";
+static const char* str_nspawn_monitor_label = "/supervisor";
+static const char* str_nspawn_payload_label = "/payload";
+
+static const char* str_snap_scope_prefix = "snap.";
+static const char* str_pod_scope_prefix = "libpod-";
+static const char* str_docker_scope_prefix = "docker-";
+
+static const char* str_service_suffix = ".service";
+static const char* str_scope_suffix = ".scope";
+
typedef struct StrBuf_state {
- char *buf;
+ char* buf;
size_t size;
size_t pos;
} StrBuf_state;
@@ -60,28 +91,7 @@ static bool Label_checkSuffix(const char* labelStart, size_t labelLen, const cha
return labelLen > strlen(expected) && String_startsWith(labelStart + labelLen - strlen(expected), expected);
}
-static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrBuf_putc_t w) {
- const char* str_slice_suffix = ".slice";
- const char* str_system_slice = "system.slice";
- const char* str_user_slice = "user.slice";
- const char* str_machine_slice = "machine.slice";
- const char* str_user_slice_prefix = "/user-";
- const char* str_system_slice_prefix = "/system-";
-
- const char* str_lxc_monitor_legacy = "lxc.monitor";
- const char* str_lxc_payload_legacy = "lxc.payload";
- const char* str_lxc_monitor_prefix = "lxc.monitor.";
- const char* str_lxc_payload_prefix = "lxc.payload.";
-
- const char* str_nspawn_scope_prefix = "machine-";
- const char* str_nspawn_monitor_label = "/supervisor";
- const char* str_nspawn_payload_label = "/payload";
-
- const char* str_snap_scope_prefix = "snap.";
-
- const char* str_service_suffix = ".service";
- const char* str_scope_suffix = ".scope";
-
+static bool CGroup_filterName_internal(const char* cgroup, StrBuf_state* s, StrBuf_putc_t w) {
while (*cgroup) {
if ('/' == *cgroup) {
while ('/' == *cgroup)
@@ -94,7 +104,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
}
const char* labelStart = cgroup;
- const char* nextSlash = strchrnul(labelStart, '/');
+ const char* nextSlash = String_strchrnul(labelStart, '/');
const size_t labelLen = nextSlash - labelStart;
if (Label_checkEqual(labelStart, labelLen, str_system_slice)) {
@@ -104,7 +114,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
return false;
if (String_startsWith(cgroup, str_system_slice_prefix)) {
- cgroup = strchrnul(cgroup + 1, '/');
+ cgroup = String_strchrnul(cgroup + 1, '/');
continue;
}
@@ -129,7 +139,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
if (!String_startsWith(cgroup, str_user_slice_prefix))
continue;
- const char* userSliceSlash = strchrnul(cgroup + strlen(str_user_slice_prefix), '/');
+ const char* userSliceSlash = String_strchrnul(cgroup + strlen(str_user_slice_prefix), '/');
const char* sliceSpec = userSliceSlash - strlen(str_slice_suffix);
if (!String_startsWith(sliceSpec, str_slice_suffix))
@@ -212,7 +222,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
while (*labelStart == '/')
labelStart++;
- nextSlash = strchrnul(labelStart, '/');
+ nextSlash = String_strchrnul(labelStart, '/');
if (nextSlash - labelStart > 0) {
if (!StrBuf_putsz(s, w, isMonitor ? "[LXC:" : "[lxc:"))
return false;
@@ -237,7 +247,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
if (String_startsWith(cgroup, "user@")) {
cgroup = nextSlash;
- while(*cgroup == '/')
+ while (*cgroup == '/')
cgroup++;
continue;
@@ -275,8 +285,8 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
cgroup += strlen(str_nspawn_payload_label);
continue;
- } else if(Label_checkPrefix(labelStart, scopeNameLen, str_snap_scope_prefix)) {
- const char* nextDot = strchrnul(labelStart + strlen(str_snap_scope_prefix), '.');
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_snap_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_snap_scope_prefix), '.');
if (!StrBuf_putsz(s, w, "!snap:"))
return false;
@@ -291,6 +301,40 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
cgroup = nextSlash;
continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_pod_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_pod_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "!pod:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_pod_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_pod_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_docker_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_docker_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "!docker:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_docker_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_docker_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
}
if (!w(s, '!'))
@@ -316,7 +360,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB
return true;
}
-char* CGroup_filterName(const char *cgroup) {
+char* CGroup_filterName(const char* cgroup) {
StrBuf_state s = {
.buf = NULL,
.size = 0,
@@ -339,3 +383,150 @@ char* CGroup_filterName(const char *cgroup) {
s.buf[s.size] = '\0';
return s.buf;
}
+
+static bool CGroup_filterContainer_internal(const char* cgroup, StrBuf_state* s, StrBuf_putc_t w) {
+ while (*cgroup) {
+ if ('/' == *cgroup) {
+ while ('/' == *cgroup)
+ cgroup++;
+
+ continue;
+ }
+
+ const char* labelStart = cgroup;
+ const char* nextSlash = String_strchrnul(labelStart, '/');
+ const size_t labelLen = nextSlash - labelStart;
+
+ if (Label_checkPrefix(labelStart, labelLen, str_lxc_payload_prefix)) {
+ const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix);
+
+ if (!StrBuf_putsz(s, w, "/lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ // LXC legacy cgroup naming
+ if (Label_checkEqual(labelStart, labelLen, str_lxc_payload_legacy)) {
+ labelStart = nextSlash;
+ while (*labelStart == '/')
+ labelStart++;
+
+ nextSlash = String_strchrnul(labelStart, '/');
+ if (nextSlash - labelStart > 0) {
+ if (!StrBuf_putsz(s, w, "/lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, labelStart, nextSlash - labelStart))
+ return false;
+
+ cgroup = nextSlash;
+ continue;
+ }
+
+ labelStart = cgroup;
+ nextSlash = labelStart + labelLen;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) {
+ const size_t scopeNameLen = labelLen - strlen(str_scope_suffix);
+
+ if (Label_checkPrefix(labelStart, scopeNameLen, str_nspawn_scope_prefix)) {
+ const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix);
+
+ const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label);
+
+ if (!is_monitor) {
+ if (!StrBuf_putsz(s, w, "/snc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen))
+ return false;
+ }
+
+ cgroup = nextSlash;
+ if (String_startsWith(nextSlash, str_nspawn_monitor_label))
+ cgroup += strlen(str_nspawn_monitor_label);
+ else if (String_startsWith(nextSlash, str_nspawn_payload_label))
+ cgroup += strlen(str_nspawn_payload_label);
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_pod_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_pod_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "/pod:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_pod_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_pod_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_docker_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_docker_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "!docker:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_docker_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_docker_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ cgroup = nextSlash;
+ }
+
+ return true;
+}
+
+char* CGroup_filterContainer(const char* cgroup) {
+ StrBuf_state s = {
+ .buf = NULL,
+ .size = 0,
+ .pos = 0,
+ };
+
+ if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_count)) {
+ return NULL;
+ }
+
+ if (!s.pos) {
+ return xStrdup("/");
+ }
+
+ s.buf = xCalloc(s.pos + 1, sizeof(char));
+ s.size = s.pos;
+ s.pos = 0;
+
+ if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_write)) {
+ free(s.buf);
+ return NULL;
+ }
+
+ s.buf[s.size] = '\0';
+ return s.buf;
+}
diff --git a/linux/CGroupUtils.h b/linux/CGroupUtils.h
index db2df7f..972a15b 100644
--- a/linux/CGroupUtils.h
+++ b/linux/CGroupUtils.h
@@ -7,10 +7,8 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
-#include <stdbool.h>
-#include <stddef.h>
-
-char* CGroup_filterName(const char *cgroup);
+char* CGroup_filterName(const char* cgroup);
+char* CGroup_filterContainer(const char* cgroup);
#endif /* HEADER_CGroupUtils */
diff --git a/linux/HugePageMeter.c b/linux/HugePageMeter.c
index 1efde2f..058ab4b 100644
--- a/linux/HugePageMeter.c
+++ b/linux/HugePageMeter.c
@@ -5,6 +5,8 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "linux/HugePageMeter.h"
#include <assert.h>
@@ -12,11 +14,11 @@ in the source distribution for its full text.
#include <stddef.h>
#include "CRT.h"
+#include "Machine.h"
#include "Macros.h"
#include "Object.h"
-#include "ProcessList.h"
#include "RichString.h"
-#include "linux/LinuxProcessList.h"
+#include "linux/LinuxMachine.h"
static const char* HugePageMeter_active_labels[4] = { NULL, NULL, NULL, NULL };
@@ -43,8 +45,8 @@ static void HugePageMeter_updateValues(Meter* this) {
memory_t usedTotal = 0;
unsigned nextUsed = 0;
- const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
- this->total = lpl->totalHugePageMem;
+ const LinuxMachine* host = (const LinuxMachine*) this->host;
+ this->total = host->totalHugePageMem;
this->values[0] = 0;
HugePageMeter_active_labels[0] = " used:";
for (unsigned i = 1; i < ARRAYSIZE(HugePageMeter_active_labels); i++) {
@@ -52,7 +54,7 @@ static void HugePageMeter_updateValues(Meter* this) {
HugePageMeter_active_labels[i] = NULL;
}
for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) {
- memory_t value = lpl->usedHugePageMem[i];
+ memory_t value = host->usedHugePageMem[i];
if (value != MEMORY_MAX) {
this->values[nextUsed] = value;
usedTotal += value;
@@ -80,7 +82,7 @@ static void HugePageMeter_display(const Object* cast, RichString* out) {
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
for (unsigned i = 0; i < ARRAYSIZE(HugePageMeter_active_labels); i++) {
- if (isnan(this->values[i])) {
+ if (!HugePageMeter_active_labels[i]) {
break;
}
RichString_appendAscii(out, CRT_colors[METER_TEXT], HugePageMeter_active_labels[i]);
diff --git a/linux/IOPriorityPanel.c b/linux/IOPriorityPanel.c
index 3e91bc4..8d0ef7b 100644
--- a/linux/IOPriorityPanel.c
+++ b/linux/IOPriorityPanel.c
@@ -5,6 +5,8 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "linux/IOPriorityPanel.h"
#include <stdbool.h>
diff --git a/linux/LibSensors.c b/linux/LibSensors.c
index 9a27fe5..5373416 100644
--- a/linux/LibSensors.c
+++ b/linux/LibSensors.c
@@ -1,6 +1,13 @@
-#include "linux/LibSensors.h"
+/*
+htop - linux/LibSensors.c
+(C) 2020-2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
-#include "config.h"
+#include "linux/LibSensors.h"
#ifdef HAVE_SENSORS_SENSORS_H
@@ -9,14 +16,15 @@
#include <errno.h>
#include <limits.h>
#include <math.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+
#include <sensors/sensors.h>
#include "Macros.h"
#include "XUtils.h"
-#include "linux/LinuxProcessList.h"
+#include "linux/LinuxMachine.h"
#ifdef BUILD_STATIC
@@ -143,7 +151,8 @@ static int tempDriverPriority(const sensors_chip_name* chip) {
void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs) {
assert(existingCPUs > 0 && existingCPUs < 16384);
- double data[existingCPUs + 1];
+
+ double* data = xMallocArray(existingCPUs + 1, sizeof(double));
for (size_t i = 0; i < existingCPUs + 1; i++)
data[i] = NAN;
@@ -200,7 +209,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns
continue;
/* If already set, e.g. Ryzen reporting platform temperature for each die, use the bigger one */
- if (isnan(data[tempID])) {
+ if (isNaN(data[tempID])) {
data[tempID] = temp;
if (tempID > 0)
coreTempCount++;
@@ -220,7 +229,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns
}
/* Only package temperature - copy to all cores */
- if (coreTempCount == 0 && !isnan(data[0])) {
+ if (coreTempCount == 0 && !isNaN(data[0])) {
for (unsigned int i = 1; i <= existingCPUs; i++)
data[i] = data[0];
@@ -229,22 +238,20 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns
}
/* No package temperature - set to max core temperature */
- if (isnan(data[0]) && coreTempCount != 0) {
- double maxTemp = NAN;
+ if (coreTempCount > 0 && isNaN(data[0])) {
+ double maxTemp = -HUGE_VAL;
for (unsigned int i = 1; i <= existingCPUs; i++) {
- if (isnan(data[i]))
- continue;
-
- maxTemp = MAXIMUM(maxTemp, data[i]);
+ if (isgreater(data[i], maxTemp)) {
+ maxTemp = data[i];
+ data[0] = data[i];
+ }
}
- data[0] = maxTemp;
-
/* Check for further adjustments */
}
/* Only temperature for core 0, maybe Ryzen - copy to all other cores */
- if (coreTempCount == 1 && !isnan(data[1])) {
+ if (coreTempCount == 1 && !isNaN(data[1])) {
for (unsigned int i = 2; i <= existingCPUs; i++)
data[i] = data[1];
@@ -264,6 +271,8 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns
out:
for (unsigned int i = 0; i <= existingCPUs; i++)
cpus[i].temperature = data[i];
+
+ free(data);
}
#endif /* HAVE_SENSORS_SENSORS_H */
diff --git a/linux/LibSensors.h b/linux/LibSensors.h
index aa89979..6f05448 100644
--- a/linux/LibSensors.h
+++ b/linux/LibSensors.h
@@ -1,7 +1,13 @@
#ifndef HEADER_LibSensors
#define HEADER_LibSensors
+/*
+htop - linux/LibSensors.h
+(C) 2020-2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
-#include "linux/LinuxProcessList.h"
+#include "linux/LinuxMachine.h"
int LibSensors_init(void);
diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c
new file mode 100644
index 0000000..ae2930d
--- /dev/null
+++ b/linux/LinuxMachine.c
@@ -0,0 +1,695 @@
+/*
+htop - LinuxMachine.c
+(C) 2014 Hisham H. Muhammad
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include "linux/LinuxMachine.h"
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "Compat.h"
+#include "CRT.h"
+#include "Macros.h"
+#include "ProcessTable.h"
+#include "Row.h"
+#include "Settings.h"
+#include "UsersTable.h"
+#include "XUtils.h"
+
+#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep
+
+#ifdef HAVE_SENSORS_SENSORS_H
+#include "LibSensors.h"
+#endif
+
+#ifndef O_PATH
+#define O_PATH 010000000 // declare for ancient glibc versions
+#endif
+
+/* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF
+ * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD
+ */
+static void LinuxMachine_updateCPUcount(LinuxMachine* this) {
+ unsigned int existing = 0, active = 0;
+ Machine* super = &this->super;
+
+ // Initialize the cpuData array before anything else.
+ if (!this->cpuData) {
+ this->cpuData = xCalloc(2, sizeof(CPUData));
+ this->cpuData[0].online = true; /* average is always "online" */
+ this->cpuData[1].online = true;
+ super->activeCPUs = 1;
+ super->existingCPUs = 1;
+ }
+
+ DIR* dir = opendir("/sys/devices/system/cpu");
+ if (!dir)
+ return;
+
+ unsigned int currExisting = super->existingCPUs;
+
+ const struct dirent* entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
+ 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;
+
+#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++;
+
+ /* readdir() iterates with no specific order */
+ unsigned int max = MAXIMUM(existing, id + 1);
+ if (max > currExisting) {
+ this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData));
+ this->cpuData[0].online = true; /* average is always "online" */
+ 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);
+ }
+
+ closedir(dir);
+
+ // return if no CPU is found
+ if (existing < 1)
+ return;
+
+#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;
+}
+
+static void LinuxMachine_scanMemoryInfo(LinuxMachine* this) {
+ Machine* host = &this->super;
+ memory_t availableMem = 0;
+ memory_t freeMem = 0;
+ memory_t totalMem = 0;
+ memory_t buffersMem = 0;
+ memory_t cachedMem = 0;
+ memory_t sharedMem = 0;
+ memory_t swapTotalMem = 0;
+ memory_t swapCacheMem = 0;
+ memory_t swapFreeMem = 0;
+ memory_t sreclaimableMem = 0;
+ memory_t zswapCompMem = 0;
+ memory_t zswapOrigMem = 0;
+
+ FILE* file = fopen(PROCMEMINFOFILE, "r");
+ if (!file)
+ CRT_fatalError("Cannot open " PROCMEMINFOFILE);
+
+ char buffer[128];
+ while (fgets(buffer, sizeof(buffer), file)) {
+
+ #define tryRead(label, variable) \
+ if (String_startsWith(buffer, label)) { \
+ memory_t parsed_; \
+ if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \
+ (variable) = parsed_; \
+ } \
+ break; \
+ } else (void) 0 /* Require a ";" after the macro use. */
+
+ switch (buffer[0]) {
+ case 'M':
+ tryRead("MemAvailable:", availableMem);
+ tryRead("MemFree:", freeMem);
+ tryRead("MemTotal:", totalMem);
+ break;
+ case 'B':
+ tryRead("Buffers:", buffersMem);
+ break;
+ case 'C':
+ tryRead("Cached:", cachedMem);
+ break;
+ case 'S':
+ switch (buffer[1]) {
+ case 'h':
+ tryRead("Shmem:", sharedMem);
+ break;
+ case 'w':
+ tryRead("SwapTotal:", swapTotalMem);
+ tryRead("SwapCached:", swapCacheMem);
+ tryRead("SwapFree:", swapFreeMem);
+ break;
+ case 'R':
+ tryRead("SReclaimable:", sreclaimableMem);
+ break;
+ }
+ break;
+ case 'Z':
+ tryRead("Zswap:", zswapCompMem);
+ tryRead("Zswapped:", zswapOrigMem);
+ break;
+ }
+
+ #undef tryRead
+ }
+
+ fclose(file);
+
+ /*
+ * Compute memory partition like procps(free)
+ * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c
+ *
+ * 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.
+ */
+ host->totalMem = totalMem;
+ host->cachedMem = cachedMem + sreclaimableMem - sharedMem;
+ host->sharedMem = sharedMem;
+ const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem;
+ host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem;
+ host->buffersMem = buffersMem;
+ host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem;
+ host->totalSwap = swapTotalMem;
+ host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem;
+ host->cachedSwap = swapCacheMem;
+ this->zswap.usedZswapComp = zswapCompMem;
+ this->zswap.usedZswapOrig = zswapOrigMem;
+}
+
+static void LinuxMachine_scanHugePages(LinuxMachine* this) {
+ this->totalHugePageMem = 0;
+ for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) {
+ this->usedHugePageMem[i] = MEMORY_MAX;
+ }
+
+ 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;
+
+ memory_t total = strtoull(content, NULL, 10);
+ 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;
+
+ memory_t free = strtoull(content, NULL, 10);
+
+ int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10);
+ assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT);
+
+ this->totalHugePageMem += total * hugePageSize;
+ this->usedHugePageMem[shift] = (total - free) * hugePageSize;
+ }
+
+ closedir(dir);
+}
+
+static void LinuxMachine_scanZramInfo(LinuxMachine* this) {
+ memory_t totalZram = 0;
+ memory_t usedZramComp = 0;
+ memory_t usedZramOrig = 0;
+
+ char mm_stat[34];
+ char disksize[34];
+
+ unsigned int i = 0;
+ for (;;) {
+ xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i);
+ xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i);
+ i++;
+ FILE* disksize_file = fopen(disksize, "r");
+ FILE* mm_stat_file = fopen(mm_stat, "r");
+ if (disksize_file == NULL || mm_stat_file == NULL) {
+ if (disksize_file) {
+ fclose(disksize_file);
+ }
+ if (mm_stat_file) {
+ fclose(mm_stat_file);
+ }
+ break;
+ }
+ memory_t size = 0;
+ memory_t orig_data_size = 0;
+ memory_t compr_data_size = 0;
+
+ if (!fscanf(disksize_file, "%llu\n", &size) ||
+ !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) {
+ fclose(disksize_file);
+ fclose(mm_stat_file);
+ break;
+ }
+
+ totalZram += size;
+ usedZramComp += compr_data_size;
+ usedZramOrig += orig_data_size;
+
+ fclose(disksize_file);
+ fclose(mm_stat_file);
+ }
+
+ this->zram.totalZram = totalZram / 1024;
+ this->zram.usedZramComp = usedZramComp / 1024;
+ this->zram.usedZramOrig = usedZramOrig / 1024;
+ if (this->zram.usedZramComp > this->zram.usedZramOrig) {
+ this->zram.usedZramComp = this->zram.usedZramOrig;
+ }
+}
+
+static void LinuxMachine_scanZfsArcstats(LinuxMachine* this) {
+ memory_t dbufSize = 0;
+ memory_t dnodeSize = 0;
+ memory_t bonusSize = 0;
+
+ FILE* file = fopen(PROCARCSTATSFILE, "r");
+ if (file == NULL) {
+ this->zfs.enabled = 0;
+ return;
+ }
+ char buffer[128];
+ while (fgets(buffer, 128, file)) {
+ #define tryRead(label, variable) \
+ if (String_startsWith(buffer, label)) { \
+ sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
+ break; \
+ } else (void) 0 /* Require a ";" after the macro use. */
+ #define tryReadFlag(label, variable, flag) \
+ if (String_startsWith(buffer, label)) { \
+ (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
+ break; \
+ } else (void) 0 /* Require a ";" after the macro use. */
+
+ switch (buffer[0]) {
+ case 'c':
+ tryRead("c_min", &this->zfs.min);
+ tryRead("c_max", &this->zfs.max);
+ tryReadFlag("compressed_size", &this->zfs.compressed, this->zfs.isCompressed);
+ break;
+ case 'u':
+ tryRead("uncompressed_size", &this->zfs.uncompressed);
+ break;
+ case 's':
+ tryRead("size", &this->zfs.size);
+ break;
+ case 'h':
+ tryRead("hdr_size", &this->zfs.header);
+ break;
+ case 'd':
+ tryRead("dbuf_size", &dbufSize);
+ tryRead("dnode_size", &dnodeSize);
+ break;
+ case 'b':
+ tryRead("bonus_size", &bonusSize);
+ break;
+ case 'a':
+ tryRead("anon_size", &this->zfs.anon);
+ break;
+ case 'm':
+ tryRead("mfu_size", &this->zfs.MFU);
+ tryRead("mru_size", &this->zfs.MRU);
+ break;
+ }
+
+ #undef tryRead
+ #undef tryReadFlag
+ }
+ fclose(file);
+
+ this->zfs.enabled = (this->zfs.size > 0 ? 1 : 0);
+ this->zfs.size /= 1024;
+ this->zfs.min /= 1024;
+ this->zfs.max /= 1024;
+ this->zfs.MFU /= 1024;
+ this->zfs.MRU /= 1024;
+ this->zfs.anon /= 1024;
+ this->zfs.header /= 1024;
+ this->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024;
+ if ( this->zfs.isCompressed ) {
+ this->zfs.compressed /= 1024;
+ this->zfs.uncompressed /= 1024;
+ }
+}
+
+static void LinuxMachine_scanCPUTime(LinuxMachine* this) {
+ const Machine* super = &this->super;
+
+ LinuxMachine_updateCPUcount(this);
+
+ FILE* file = fopen(PROCSTATFILE, "r");
+ if (!file)
+ CRT_fatalError("Cannot open " PROCSTATFILE);
+
+ unsigned int lastAdjCpuId = 0;
+
+ for (unsigned int i = 0; i <= super->existingCPUs; i++) {
+ char buffer[PROC_LINE_LENGTH + 1];
+ unsigned long long int usertime, nicetime, systemtime, idletime;
+ unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0;
+
+ const char* ok = fgets(buffer, sizeof(buffer), file);
+ if (!ok)
+ break;
+
+ // 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;
+ if (i == 0) {
+ (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
+ adjCpuId = 0;
+ } else {
+ 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);
+ 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));
+ }
+ lastAdjCpuId = adjCpuId;
+
+ // Guest time is already accounted in usertime
+ usertime -= guest;
+ nicetime -= guestnice;
+ // Fields existing on kernels >= 2.6
+ // (and RHEL's patched kernel 2.4...)
+ 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;
+ CPUData* cpuData = &(this->cpuData[adjCpuId]);
+ // 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.
+ 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);
+ 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;
+ }
+
+ this->period = (double)this->cpuData[0].totalPeriod / super->activeCPUs;
+
+ char buffer[PROC_LINE_LENGTH + 1];
+ while (fgets(buffer, sizeof(buffer), file)) {
+ if (String_startsWith(buffer, "procs_running")) {
+ ProcessTable* pt = (ProcessTable*) super->processTable;
+ pt->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10);
+ break;
+ }
+ }
+
+ fclose(file);
+}
+
+static int scanCPUFrequencyFromSysCPUFreq(LinuxMachine* this) {
+ const Machine* super = &this->super;
+ int numCPUsWithFrequency = 0;
+ unsigned long totalFrequency = 0;
+
+ /*
+ * 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;
+ }
+
+ for (unsigned int i = 0; i < super->existingCPUs; ++i) {
+ if (!Machine_isCPUonline(super, i))
+ continue;
+
+ char pathBuffer[64];
+ xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
+
+ struct timespec start;
+ if (i == 0)
+ clock_gettime(CLOCK_MONOTONIC, &start);
+
+ FILE* file = fopen(pathBuffer, "r");
+ if (!file)
+ return -errno;
+
+ unsigned long frequency;
+ if (fscanf(file, "%lu", &frequency) == 1) {
+ /* convert kHz to MHz */
+ frequency = frequency / 1000;
+ this->cpuData[i + 1].frequency = frequency;
+ numCPUsWithFrequency++;
+ totalFrequency += frequency;
+ }
+
+ fclose(file);
+
+ 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;
+ }
+ }
+ }
+
+ if (numCPUsWithFrequency > 0)
+ this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
+
+ return 0;
+}
+
+static void scanCPUFrequencyFromCPUinfo(LinuxMachine* this) {
+ const Machine* super = &this->super;
+
+ FILE* file = fopen(PROCCPUINFOFILE, "r");
+ if (file == NULL)
+ return;
+
+ int numCPUsWithFrequency = 0;
+ double totalFrequency = 0;
+ int cpuid = -1;
+
+ while (!feof(file)) {
+ double frequency;
+ char buffer[PROC_LINE_LENGTH];
+
+ if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
+ break;
+
+ if (sscanf(buffer, "processor : %d", &cpuid) == 1) {
+ continue;
+ } else if (
+ (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
+ (sscanf(buffer, "clock : %lfMHz", &frequency) == 1)
+ ) {
+ if (cpuid < 0 || (unsigned int)cpuid > (super->existingCPUs - 1)) {
+ continue;
+ }
+
+ CPUData* cpuData = &(this->cpuData[cpuid + 1]);
+ /* do not override sysfs data */
+ if (!isNonnegative(cpuData->frequency)) {
+ cpuData->frequency = frequency;
+ }
+ numCPUsWithFrequency++;
+ totalFrequency += frequency;
+ } else if (buffer[0] == '\n') {
+ cpuid = -1;
+ }
+ }
+ fclose(file);
+
+ if (numCPUsWithFrequency > 0) {
+ this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency;
+ }
+}
+
+static void LinuxMachine_scanCPUFrequency(LinuxMachine* this) {
+ const Machine* super = &this->super;
+
+ for (unsigned int i = 0; i <= super->existingCPUs; i++)
+ this->cpuData[i].frequency = NAN;
+
+ if (scanCPUFrequencyFromSysCPUFreq(this) == 0)
+ return;
+
+ scanCPUFrequencyFromCPUinfo(this);
+}
+
+void Machine_scan(Machine* super) {
+ LinuxMachine* this = (LinuxMachine*) super;
+
+ LinuxMachine_scanMemoryInfo(this);
+ LinuxMachine_scanHugePages(this);
+ LinuxMachine_scanZfsArcstats(this);
+ LinuxMachine_scanZramInfo(this);
+ LinuxMachine_scanCPUTime(this);
+
+ const Settings* settings = super->settings;
+ if (settings->showCPUFrequency)
+ LinuxMachine_scanCPUFrequency(this);
+
+ #ifdef HAVE_SENSORS_SENSORS_H
+ if (settings->showCPUTemperature)
+ LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs);
+ #endif
+}
+
+Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
+ LinuxMachine* this = xCalloc(1, sizeof(LinuxMachine));
+ Machine* super = &this->super;
+
+ Machine_init(super, usersTable, userId);
+
+ // Initialize page size
+ if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1)
+ CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
+ this->pageSizeKB = this->pageSize / ONE_K;
+
+ // Initialize clock ticks
+ if ((this->jiffies = sysconf(_SC_CLK_TCK)) == -1)
+ CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
+
+ // Read btime (the kernel boot time, as number of seconds since the epoch)
+ FILE* statfile = fopen(PROCSTATFILE, "r");
+ if (statfile == NULL)
+ CRT_fatalError("Cannot open " PROCSTATFILE);
+
+ this->boottime = -1;
+
+ 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", &this->boottime) == 1)
+ break;
+ CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
+ }
+ fclose(statfile);
+
+ if (this->boottime == -1)
+ CRT_fatalError("No btime in " PROCSTATFILE);
+
+ // Initialize CPU count
+ LinuxMachine_updateCPUcount(this);
+
+ return super;
+}
+
+void Machine_delete(Machine* super) {
+ LinuxMachine* this = (LinuxMachine*) super;
+ Machine_done(super);
+ free(this->cpuData);
+ free(this);
+}
+
+bool Machine_isCPUonline(const Machine* super, unsigned int id) {
+ const LinuxMachine* this = (const LinuxMachine*) super;
+
+ assert(id < super->existingCPUs);
+ return this->cpuData[id + 1].online;
+}
diff --git a/linux/LinuxProcessList.h b/linux/LinuxMachine.h
index 6c2f7db..309b485 100644
--- a/linux/LinuxProcessList.h
+++ b/linux/LinuxMachine.h
@@ -1,21 +1,17 @@
-#ifndef HEADER_LinuxProcessList
-#define HEADER_LinuxProcessList
+#ifndef HEADER_LinuxMachine
+#define HEADER_LinuxMachine
/*
-htop - LinuxProcessList.h
+htop - LinuxMachine.h
(C) 2014 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h"
-
#include <stdbool.h>
-#include <sys/types.h>
-#include "Hashtable.h"
-#include "ProcessList.h"
-#include "UsersTable.h"
-#include "ZramStats.h"
+#include "Machine.h"
+#include "linux/ZramStats.h"
+#include "linux/ZswapStats.h"
#include "zfs/ZfsArcStats.h"
#define HTOP_HUGEPAGE_BASE_SHIFT 16
@@ -57,26 +53,20 @@ typedef struct CPUData_ {
bool online;
} CPUData;
-typedef struct TtyDriver_ {
- char* path;
- unsigned int major;
- unsigned int minorFrom;
- unsigned int minorTo;
-} TtyDriver;
+typedef struct LinuxMachine_ {
+ Machine super;
-typedef struct LinuxProcessList_ {
- ProcessList super;
+ long jiffies;
+ int pageSize;
+ int pageSizeKB;
- CPUData* cpuData;
+ /* see Linux kernel source for further detail, fs/proc/stat.c */
+ unsigned int runningTasks; /* procs_running from /proc/stat */
+ long long boottime; /* btime field from /proc/stat */
- TtyDriver* ttyDrivers;
- bool haveSmapsRollup;
- bool haveAutogroup;
+ double period;
- #ifdef HAVE_DELAYACCT
- struct nl_sock* netlink_socket;
- int netlink_family;
- #endif
+ CPUData* cpuData;
memory_t totalHugePageMem;
memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT];
@@ -85,7 +75,8 @@ typedef struct LinuxProcessList_ {
ZfsArcStats zfs;
ZramStats zram;
-} LinuxProcessList;
+ ZswapStats zswap;
+} LinuxMachine;
#ifndef PROCDIR
#define PROCDIR "/proc"
@@ -115,12 +106,4 @@ typedef struct LinuxProcessList_ {
#define PROC_LINE_LENGTH 4096
#endif
-ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
-
-void ProcessList_delete(ProcessList* pl);
-
-void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
-
-bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id);
-
#endif
diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c
index 92be326..dc4f259 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -6,9 +6,13 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "linux/LinuxProcess.h"
+#include <assert.h>
#include <math.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <syscall.h>
@@ -19,14 +23,14 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProvideCurses.h"
#include "RichString.h"
+#include "RowField.h"
+#include "Scheduling.h"
+#include "Settings.h"
#include "XUtils.h"
#include "linux/IOPriority.h"
+#include "linux/LinuxMachine.h"
-/* semi-global */
-int pageSize;
-int pageSizeKB;
-
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
@@ -53,8 +57,9 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[M_SHARE] = { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, .defaultSortDesc = true, },
- [M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, .defaultSortDesc = true, },
- [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, },
+ [M_PRIV] = { .name = "M_PRIV", .title = " PRIV ", .description = "The private memory size of the process - resident set size minus shared memory", .flags = 0, .defaultSortDesc = true, },
+ [M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the .text segment of the process (CODE)", .flags = 0, .defaultSortDesc = true, },
+ [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the .data segment plus stack usage of the process (DATA)", .flags = 0, .defaultSortDesc = true, },
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
@@ -78,11 +83,12 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[RBYTES] = { .name = "RBYTES", .title = " IO_R ", .description = "Bytes of read(2) I/O for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[WBYTES] = { .name = "WBYTES", .title = " IO_W ", .description = "Bytes of write(2) I/O for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[CNCLWB] = { .name = "CNCLWB", .title = " IO_C ", .description = "Bytes of cancelled write(2) I/O", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
- [IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
+ [IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[CGROUP] = { .name = "CGROUP", .title = "CGROUP (raw)", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, .autoWidth = true, },
[CCGROUP] = { .name = "CCGROUP", .title = "CGROUP (compressed)", .description = "Which cgroup the process is in (condensed to essentials)", .flags = PROCESS_FLAG_LINUX_CGROUP, .autoWidth = true, },
+ [CONTAINER] = { .name = "CONTAINER", .title = "CONTAINER", .description = "Name of the container the process is in (guessed by heuristics)", .flags = PROCESS_FLAG_LINUX_CGROUP, .autoWidth = true, },
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT
@@ -100,18 +106,22 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, },
[AUTOGROUP_ID] = { .name = "AUTOGROUP_ID", .title = "AGRP", .description = "The autogroup identifier of the process", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, },
[AUTOGROUP_NICE] = { .name = "AUTOGROUP_NICE", .title = " ANI", .description = "Nice value (the higher the value, the more other processes take priority) associated with the process autogroup", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, },
+#ifdef SCHEDULER_SUPPORT
+ [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, },
+#endif
};
-Process* LinuxProcess_new(const Settings* settings) {
+Process* LinuxProcess_new(const Machine* host) {
LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess));
Object_setClass(this, Class(LinuxProcess));
- Process_init(&this->super, settings);
+ Process_init(&this->super, host);
return &this->super;
}
void Process_delete(Object* cast) {
LinuxProcess* this = (LinuxProcess*) cast;
Process_done((Process*)cast);
+ free(this->container_short);
free(this->cgroup_short);
free(this->cgroup);
#ifdef HAVE_OPENVZ
@@ -142,22 +152,29 @@ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) {
#define SYS_ioprio_set __NR_ioprio_set
#endif
-IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
+IOPriority LinuxProcess_updateIOPriority(Process* p) {
IOPriority ioprio = 0;
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
#ifdef SYS_ioprio_get
- ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, this->super.pid);
+ ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, Process_getPid(p));
#endif
+ LinuxProcess* this = (LinuxProcess*) p;
this->ioPriority = ioprio;
return ioprio;
}
-bool LinuxProcess_setIOPriority(Process* this, Arg ioprio) {
+static bool LinuxProcess_setIOPriority(Process* p, Arg ioprio) {
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
#ifdef SYS_ioprio_set
- syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->pid, ioprio.i);
+ syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, Process_getPid(p), ioprio.i);
#endif
- return (LinuxProcess_updateIOPriority((LinuxProcess*)this) == ioprio.i);
+ return LinuxProcess_updateIOPriority(p) == ioprio.i;
+}
+
+bool LinuxProcess_rowSetIOPriority(Row* super, Arg ioprio) {
+ Process* p = (Process*) super;
+ assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class));
+ return LinuxProcess_setIOPriority(p, ioprio);
}
bool LinuxProcess_isAutogroupEnabled(void) {
@@ -167,9 +184,10 @@ bool LinuxProcess_isAutogroupEnabled(void) {
return buf[0] == '1';
}
-bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) {
+static bool LinuxProcess_changeAutogroupPriorityBy(Process* p, Arg delta) {
char buffer[256];
- xSnprintf(buffer, sizeof(buffer), PROCDIR "/%d/autogroup", this->pid);
+ pid_t pid = Process_getPid(p);
+ xSnprintf(buffer, sizeof(buffer), PROCDIR "/%d/autogroup", pid);
FILE* file = fopen(buffer, "r+");
if (!file)
@@ -178,69 +196,79 @@ bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) {
long int identity;
int nice;
int ok = fscanf(file, "/autogroup-%ld nice %d", &identity, &nice);
- bool success;
- if (ok == 2) {
- rewind(file);
+ bool success = false;
+ if (ok == 2 && fseek(file, 0L, SEEK_SET) == 0) {
xSnprintf(buffer, sizeof(buffer), "%d", nice + delta.i);
success = fputs(buffer, file) > 0;
- } else {
- success = false;
}
fclose(file);
return success;
}
-static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) {
- const LinuxProcess* lp = (const LinuxProcess*) this;
- bool coloring = this->settings->highlightMegabytes;
+bool LinuxProcess_rowChangeAutogroupPriorityBy(Row* super, Arg delta) {
+ Process* p = (Process*) super;
+ assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class));
+ return LinuxProcess_changeAutogroupPriorityBy(p, delta);
+}
+
+static double LinuxProcess_totalIORate(const LinuxProcess* lp) {
+ double totalRate = NAN;
+ if (isNonnegative(lp->io_rate_read_bps)) {
+ totalRate = lp->io_rate_read_bps;
+ if (isNonnegative(lp->io_rate_write_bps)) {
+ totalRate += lp->io_rate_write_bps;
+ }
+ } else if (isNonnegative(lp->io_rate_write_bps)) {
+ totalRate = lp->io_rate_write_bps;
+ }
+ return totalRate;
+}
+
+static void LinuxProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
+ const Process* this = (const Process*) super;
+ const LinuxProcess* lp = (const LinuxProcess*) super;
+ const Machine* host = (const Machine*) super->host;
+ const LinuxMachine* lhost = (const LinuxMachine*) super->host;
+
+ bool coloring = host->settings->highlightMegabytes;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
size_t n = sizeof(buffer) - 1;
+
switch (field) {
- case CMINFLT: Process_printCount(str, lp->cminflt, coloring); return;
- case CMAJFLT: Process_printCount(str, lp->cmajflt, coloring); return;
- case M_DRS: Process_printBytes(str, lp->m_drs * pageSize, coloring); return;
+ case CMINFLT: Row_printCount(str, lp->cminflt, coloring); return;
+ case CMAJFLT: Row_printCount(str, lp->cmajflt, coloring); return;
+ case M_DRS: Row_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return;
case M_LRS:
if (lp->m_lrs) {
- Process_printBytes(str, lp->m_lrs * pageSize, coloring);
+ Row_printBytes(str, lp->m_lrs * lhost->pageSize, coloring);
return;
}
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
break;
- case M_TRS: Process_printBytes(str, lp->m_trs * pageSize, coloring); return;
- case M_SHARE: Process_printBytes(str, lp->m_share * pageSize, coloring); return;
- case M_PSS: Process_printKBytes(str, lp->m_pss, coloring); return;
- case M_SWAP: Process_printKBytes(str, lp->m_swap, coloring); return;
- case M_PSSWP: Process_printKBytes(str, lp->m_psswp, coloring); return;
- case UTIME: Process_printTime(str, lp->utime, coloring); return;
- case STIME: Process_printTime(str, lp->stime, coloring); return;
- case CUTIME: Process_printTime(str, lp->cutime, coloring); return;
- case CSTIME: Process_printTime(str, lp->cstime, coloring); return;
- case RCHAR: Process_printBytes(str, lp->io_rchar, coloring); return;
- case WCHAR: Process_printBytes(str, lp->io_wchar, coloring); return;
- case SYSCR: Process_printCount(str, lp->io_syscr, coloring); return;
- case SYSCW: Process_printCount(str, lp->io_syscw, coloring); return;
- case RBYTES: Process_printBytes(str, lp->io_read_bytes, coloring); return;
- case WBYTES: Process_printBytes(str, lp->io_write_bytes, coloring); return;
- case CNCLWB: Process_printBytes(str, lp->io_cancelled_write_bytes, coloring); return;
- case IO_READ_RATE: Process_printRate(str, lp->io_rate_read_bps, coloring); return;
- case IO_WRITE_RATE: Process_printRate(str, lp->io_rate_write_bps, coloring); return;
- case IO_RATE: {
- double totalRate;
- if (!isnan(lp->io_rate_read_bps) && !isnan(lp->io_rate_write_bps))
- totalRate = lp->io_rate_read_bps + lp->io_rate_write_bps;
- else if (!isnan(lp->io_rate_read_bps))
- totalRate = lp->io_rate_read_bps;
- else if (!isnan(lp->io_rate_write_bps))
- totalRate = lp->io_rate_write_bps;
- else
- totalRate = NAN;
- Process_printRate(str, totalRate, coloring);
- return;
- }
+ case M_TRS: Row_printBytes(str, lp->m_trs * lhost->pageSize, coloring); return;
+ case M_SHARE: Row_printBytes(str, lp->m_share * lhost->pageSize, coloring); return;
+ case M_PRIV: Row_printKBytes(str, lp->m_priv, coloring); return;
+ case M_PSS: Row_printKBytes(str, lp->m_pss, coloring); return;
+ case M_SWAP: Row_printKBytes(str, lp->m_swap, coloring); return;
+ case M_PSSWP: Row_printKBytes(str, lp->m_psswp, coloring); return;
+ case UTIME: Row_printTime(str, lp->utime, coloring); return;
+ case STIME: Row_printTime(str, lp->stime, coloring); return;
+ case CUTIME: Row_printTime(str, lp->cutime, coloring); return;
+ case CSTIME: Row_printTime(str, lp->cstime, coloring); return;
+ case RCHAR: Row_printBytes(str, lp->io_rchar, coloring); return;
+ case WCHAR: Row_printBytes(str, lp->io_wchar, coloring); return;
+ case SYSCR: Row_printCount(str, lp->io_syscr, coloring); return;
+ case SYSCW: Row_printCount(str, lp->io_syscw, coloring); return;
+ case RBYTES: Row_printBytes(str, lp->io_read_bytes, coloring); return;
+ case WBYTES: Row_printBytes(str, lp->io_write_bytes, coloring); return;
+ case CNCLWB: Row_printBytes(str, lp->io_cancelled_write_bytes, coloring); return;
+ case IO_READ_RATE: Row_printRate(str, lp->io_rate_read_bps, coloring); return;
+ case IO_WRITE_RATE: Row_printRate(str, lp->io_rate_write_bps, coloring); return;
+ case IO_RATE: Row_printRate(str, LinuxProcess_totalIORate(lp), coloring); return;
#ifdef HAVE_OPENVZ
case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break;
case VPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, lp->vpid); break;
@@ -248,8 +276,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
#ifdef HAVE_VSERVER
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
#endif
- case CGROUP: xSnprintf(buffer, n, "%-*.*s ", Process_fieldWidths[CGROUP], Process_fieldWidths[CGROUP], lp->cgroup ? lp->cgroup : "N/A"); break;
- case CCGROUP: xSnprintf(buffer, n, "%-*.*s ", Process_fieldWidths[CCGROUP], Process_fieldWidths[CCGROUP], lp->cgroup_short ? lp->cgroup_short : (lp->cgroup ? lp->cgroup : "N/A")); break;
+ case CGROUP: xSnprintf(buffer, n, "%-*.*s ", Row_fieldWidths[CGROUP], Row_fieldWidths[CGROUP], lp->cgroup ? lp->cgroup : "N/A"); break;
+ case CCGROUP: xSnprintf(buffer, n, "%-*.*s ", Row_fieldWidths[CCGROUP], Row_fieldWidths[CCGROUP], lp->cgroup_short ? lp->cgroup_short : (lp->cgroup ? lp->cgroup : "N/A")); break;
+ case CONTAINER: xSnprintf(buffer, n, "%-*.*s ", Row_fieldWidths[CONTAINER], Row_fieldWidths[CONTAINER], lp->container_short ? lp->container_short : "N/A"); break;
case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break;
case IO_PRIORITY: {
int klass = IOPriority_class(lp->ioPriority);
@@ -270,9 +299,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
break;
}
#ifdef HAVE_DELAYACCT
- case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 5, &attr); break;
- case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 5, &attr); break;
- case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break;
+ case PERCENT_CPU_DELAY: Row_printPercentage(lp->cpu_delay_percent, buffer, n, 5, &attr); break;
+ case PERCENT_IO_DELAY: Row_printPercentage(lp->blkio_delay_percent, buffer, n, 5, &attr); break;
+ case PERCENT_SWAP_DELAY: Row_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break;
#endif
case CTXT:
if (lp->ctxt_diff > 1000) {
@@ -280,7 +309,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
}
xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
break;
- case SECATTR: snprintf(buffer, n, "%-*.*s ", Process_fieldWidths[SECATTR], Process_fieldWidths[SECATTR], lp->secattr ? lp->secattr : "N/A"); break;
+ case SECATTR: snprintf(buffer, n, "%-*.*s ", Row_fieldWidths[SECATTR], Row_fieldWidths[SECATTR], lp->secattr ? lp->secattr : "N/A"); break;
case AUTOGROUP_ID:
if (lp->autogroup_id != -1) {
xSnprintf(buffer, n, "%4ld ", lp->autogroup_id);
@@ -293,8 +322,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
if (lp->autogroup_id != -1) {
xSnprintf(buffer, n, "%3d ", lp->autogroup_nice);
attr = lp->autogroup_nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
- : lp->autogroup_nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
- : CRT_colors[PROCESS_SHADOW];
+ : lp->autogroup_nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
+ : CRT_colors[PROCESS_SHADOW];
} else {
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "N/A ");
@@ -304,14 +333,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
Process_writeField(this, str, field);
return;
}
- RichString_appendAscii(str, attr, buffer);
-}
-static double adjustNaN(double num) {
- if (isnan(num))
- return -0.0005;
-
- return num;
+ RichString_appendAscii(str, attr, buffer);
}
static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
@@ -327,6 +350,8 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
return SPACESHIP_NUMBER(p1->m_trs, p2->m_trs);
case M_SHARE:
return SPACESHIP_NUMBER(p1->m_share, p2->m_share);
+ case M_PRIV:
+ return SPACESHIP_NUMBER(p1->m_priv, p2->m_priv);
case M_PSS:
return SPACESHIP_NUMBER(p1->m_pss, p2->m_pss);
case M_SWAP:
@@ -356,11 +381,11 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
case CNCLWB:
return SPACESHIP_NUMBER(p1->io_cancelled_write_bytes, p2->io_cancelled_write_bytes);
case IO_READ_RATE:
- return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_read_bps), adjustNaN(p2->io_rate_read_bps));
+ return compareRealNumbers(p1->io_rate_read_bps, p2->io_rate_read_bps);
case IO_WRITE_RATE:
- return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_write_bps), adjustNaN(p2->io_rate_write_bps));
+ return compareRealNumbers(p1->io_rate_write_bps, p2->io_rate_write_bps);
case IO_RATE:
- return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_read_bps) + adjustNaN(p1->io_rate_write_bps), adjustNaN(p2->io_rate_read_bps) + adjustNaN(p2->io_rate_write_bps));
+ return compareRealNumbers(LinuxProcess_totalIORate(p1), LinuxProcess_totalIORate(p2));
#ifdef HAVE_OPENVZ
case CTID:
return SPACESHIP_NULLSTR(p1->ctid, p2->ctid);
@@ -375,15 +400,17 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup);
case CCGROUP:
return SPACESHIP_NULLSTR(p1->cgroup_short, p2->cgroup_short);
+ case CONTAINER:
+ return SPACESHIP_NULLSTR(p1->container_short, p2->container_short);
case OOM:
return SPACESHIP_NUMBER(p1->oom, p2->oom);
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY:
- return SPACESHIP_NUMBER(p1->cpu_delay_percent, p2->cpu_delay_percent);
+ return compareRealNumbers(p1->cpu_delay_percent, p2->cpu_delay_percent);
case PERCENT_IO_DELAY:
- return SPACESHIP_NUMBER(p1->blkio_delay_percent, p2->blkio_delay_percent);
+ return compareRealNumbers(p1->blkio_delay_percent, p2->blkio_delay_percent);
case PERCENT_SWAP_DELAY:
- return SPACESHIP_NUMBER(p1->swapin_delay_percent, p2->swapin_delay_percent);
+ return compareRealNumbers(p1->swapin_delay_percent, p2->swapin_delay_percent);
#endif
case IO_PRIORITY:
return SPACESHIP_NUMBER(LinuxProcess_effectiveIOPriority(p1), LinuxProcess_effectiveIOPriority(p2));
@@ -402,11 +429,18 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
const ProcessClass LinuxProcess_class = {
.super = {
- .extends = Class(Process),
- .display = Process_display,
- .delete = Process_delete,
- .compare = Process_compare
+ .super = {
+ .extends = Class(Process),
+ .display = Row_display,
+ .delete = Process_delete,
+ .compare = Process_compare
+ },
+ .isHighlighted = Process_rowIsHighlighted,
+ .isVisible = Process_rowIsVisible,
+ .matchesFilter = Process_rowMatchesFilter,
+ .compareByParent = Process_compareByParent,
+ .sortKeyString = Process_rowGetSortKey,
+ .writeField = LinuxProcess_rowWriteField
},
- .writeField = LinuxProcess_writeField,
.compareByKey = LinuxProcess_compareByKey
};
diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h
index 3e5d380..388e50d 100644
--- a/linux/LinuxProcess.h
+++ b/linux/LinuxProcess.h
@@ -8,15 +8,14 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h" // IWYU pragma: keep
-
#include <stdbool.h>
-#include <sys/types.h>
-#include "linux/IOPriority.h"
+#include "Machine.h"
#include "Object.h"
#include "Process.h"
-#include "Settings.h"
+#include "Row.h"
+
+#include "linux/IOPriority.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x00000100
@@ -41,6 +40,7 @@ typedef struct LinuxProcess_ {
unsigned long long int cutime;
unsigned long long int cstime;
long m_share;
+ long m_priv;
long m_pss;
long m_swap;
long m_psswp;
@@ -90,6 +90,7 @@ typedef struct LinuxProcess_ {
#endif
char* cgroup;
char* cgroup_short;
+ char* container_short;
unsigned int oom;
#ifdef HAVE_DELAYACCT
unsigned long long int delay_read_time;
@@ -118,17 +119,17 @@ extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern const ProcessClass LinuxProcess_class;
-Process* LinuxProcess_new(const Settings* settings);
+Process* LinuxProcess_new(const Machine* host);
void Process_delete(Object* cast);
-IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
+IOPriority LinuxProcess_updateIOPriority(Process* proc);
-bool LinuxProcess_setIOPriority(Process* this, Arg ioprio);
+bool LinuxProcess_rowSetIOPriority(Row* super, Arg ioprio);
bool LinuxProcess_isAutogroupEnabled(void);
-bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta);
+bool LinuxProcess_rowChangeAutogroupPriorityBy(Row* super, Arg delta);
bool Process_isThread(const Process* this);
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessTable.c
index 45b045c..039a64e 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessTable.c
@@ -1,5 +1,5 @@
/*
-htop - LinuxProcessList.c
+htop - LinuxProcessTable.c
(C) 2014 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
@@ -7,24 +7,21 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep
-#include "linux/LinuxProcessList.h"
+#include "linux/LinuxProcessTable.h"
#include <assert.h>
+#include <ctype.h>
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
-#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <strings.h>
-#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
-#include <sys/types.h>
#ifdef HAVE_DELAYACCT
#include <linux/netlink.h>
@@ -39,13 +36,20 @@ in the source distribution for its full text.
#endif
#include "Compat.h"
-#include "CRT.h"
+#include "Hashtable.h"
+#include "Machine.h"
#include "Macros.h"
#include "Object.h"
#include "Process.h"
+#include "Row.h"
+#include "RowField.h"
+#include "Scheduling.h"
#include "Settings.h"
+#include "Table.h"
+#include "UsersTable.h"
#include "XUtils.h"
#include "linux/CGroupUtils.h"
+#include "linux/LinuxMachine.h"
#include "linux/LinuxProcess.h"
#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep
@@ -55,23 +59,11 @@ in the source distribution for its full text.
#include <sys/sysmacros.h>
#endif
-#ifdef HAVE_SENSORS_SENSORS_H
-#include "LibSensors.h"
-#endif
-
-#ifndef O_PATH
-#define O_PATH 010000000 // declare for ancient glibc versions
-#endif
-
/* Not exposed yet. Defined at include/linux/sched.h */
#ifndef PF_KTHREAD
#define PF_KTHREAD 0x00200000
#endif
-static long long btime = -1;
-
-static long jiffy;
-
static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
assert(String_eq(mode, "r")); /* only currently supported mode */
@@ -86,6 +78,48 @@ static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* m
return stream;
}
+static inline uint64_t fast_strtoull_dec(char** str, int maxlen) {
+ register uint64_t result = 0;
+
+ if (!maxlen)
+ --maxlen;
+
+ while (maxlen-- && **str >= '0' && **str <= '9') {
+ result *= 10;
+ result += **str - '0';
+ (*str)++;
+ }
+
+ return result;
+}
+
+static inline uint64_t fast_strtoull_hex(char** str, int maxlen) {
+ register uint64_t result = 0;
+ register int nibble, letter;
+ const long valid_mask = 0x03FF007E;
+
+ if (!maxlen)
+ --maxlen;
+
+ while (maxlen--) {
+ nibble = (unsigned char)**str;
+ if (!(valid_mask & (1 << (nibble & 0x1F))))
+ break;
+ if ((nibble < '0') || (nibble & ~0x20) > 'F')
+ break;
+ letter = (nibble & 0x40) ? 'A' - '9' - 1 : 0;
+ nibble &=~0x20; // to upper
+ nibble ^= 0x10; // switch letters and digits
+ nibble -= letter;
+ nibble &= 0x0f;
+ result <<= 4;
+ result += (uint64_t)nibble;
+ (*str)++;
+ }
+
+ return result;
+}
+
static int sortTtyDrivers(const void* va, const void* vb) {
const TtyDriver* a = (const TtyDriver*) va;
const TtyDriver* b = (const TtyDriver*) vb;
@@ -97,7 +131,7 @@ static int sortTtyDrivers(const void* va, const void* vb) {
return SPACESHIP_NUMBER(a->minorFrom, b->minorFrom);
}
-static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
+static void LinuxProcessTable_initTtyDrivers(LinuxProcessTable* this) {
TtyDriver* ttyDrivers;
char buf[16384];
@@ -153,7 +187,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
#ifdef HAVE_DELAYACCT
-static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
+static void LinuxProcessTable_initNetlinkSocket(LinuxProcessTable* this) {
this->netlink_socket = nl_socket_alloc();
if (this->netlink_socket == NULL) {
return;
@@ -166,171 +200,24 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
#endif
-static unsigned int scanAvailableCPUsFromCPUinfo(LinuxProcessList* this) {
- FILE* file = fopen(PROCCPUINFOFILE, "r");
- if (file == NULL)
- return this->super.existingCPUs;
-
- unsigned int availableCPUs = 0;
-
- while (!feof(file)) {
- char buffer[PROC_LINE_LENGTH];
-
- if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
- break;
-
- if (String_startsWith(buffer, "processor"))
- availableCPUs++;
- }
-
- fclose(file);
-
- return availableCPUs ? availableCPUs : 1;
-}
-
-static void LinuxProcessList_updateCPUcount(ProcessList* super) {
- /* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF
- * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD
- */
-
- LinuxProcessList* this = (LinuxProcessList*) super;
- unsigned int existing = 0, active = 0;
-
- // Initialize the cpuData array before anything else.
- if (!this->cpuData) {
- this->cpuData = xCalloc(2, sizeof(CPUData));
- this->cpuData[0].online = true; /* average is always "online" */
- this->cpuData[1].online = true;
- super->activeCPUs = 1;
- super->existingCPUs = 1;
- }
-
- DIR* dir = opendir("/sys/devices/system/cpu");
- if (!dir)
- return;
-
- unsigned int currExisting = super->existingCPUs;
-
- const struct dirent* entry;
- while ((entry = readdir(dir)) != NULL) {
- if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
- 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;
-
-#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++;
-
- /* readdir() iterates with no specific order */
- unsigned int max = MAXIMUM(existing, id + 1);
- if (max > currExisting) {
- this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData));
- this->cpuData[0].online = true; /* average is always "online" */
- 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);
- }
-
- closedir(dir);
-
- // return if no CPU is found
- if (existing < 1)
- return;
-
- if (Running_containerized) {
- /* LXC munges /proc/cpuinfo but not the /sys/devices/system/cpu/ files,
- * so limit the visible CPUs to what the guest has been configured to see: */
- currExisting = active = scanAvailableCPUsFromCPUinfo(this);
- }
-
-#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(Running_containerized || (existing == currExisting));
- super->existingCPUs = currExisting;
-}
-
-ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
- LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
- ProcessList* pl = &(this->super);
+ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
+ LinuxProcessTable* this = xCalloc(1, sizeof(LinuxProcessTable));
+ Object_setClass(this, Class(ProcessTable));
- ProcessList_init(pl, Class(LinuxProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId);
- LinuxProcessList_initTtyDrivers(this);
+ ProcessTable* super = &this->super;
+ ProcessTable_init(super, Class(LinuxProcess), host, pidMatchList);
- // Initialize page size
- pageSize = sysconf(_SC_PAGESIZE);
- if (pageSize == -1)
- CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
- pageSizeKB = pageSize / ONE_K;
-
- // Initialize clock ticks
- jiffy = sysconf(_SC_CLK_TCK);
- if (jiffy == -1)
- CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
+ LinuxProcessTable_initTtyDrivers(this);
// Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+)
this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0);
- // Read btime (the kernel boot time, as number of seconds since the epoch)
- 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);
- }
-
- fclose(statfile);
-
- if (btime == -1)
- CRT_fatalError("No btime in " PROCSTATFILE);
-
- // Initialize CPU count
- LinuxProcessList_updateCPUcount(pl);
-
- return pl;
+ return super;
}
-void ProcessList_delete(ProcessList* pl) {
- LinuxProcessList* this = (LinuxProcessList*) pl;
- ProcessList_done(pl);
- free(this->cpuData);
+void ProcessTable_delete(Object* cast) {
+ LinuxProcessTable* this = (LinuxProcessTable*) cast;
+ ProcessTable_done(&this->super);
if (this->ttyDrivers) {
for (int i = 0; this->ttyDrivers[i].path; i++) {
free(this->ttyDrivers[i].path);
@@ -346,12 +233,12 @@ void ProcessList_delete(ProcessList* pl) {
free(this);
}
-static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) {
- return t * 100 / jiffy;
+static inline unsigned long long LinuxProcessTable_adjustTime(const LinuxMachine* lhost, unsigned long long t) {
+ return t * 100 / lhost->jiffies;
}
/* Taken from: https://github.com/torvalds/linux/blob/64570fbc14f8d7cb3fe3995f20e26bc25ce4b2cc/fs/proc/array.c#L120 */
-static inline ProcessState LinuxProcessList_getProcessState(char state) {
+static inline ProcessState LinuxProcessTable_getProcessState(char state) {
switch (state) {
case 'S': return SLEEPING;
case 'X': return DEFUNCT;
@@ -366,16 +253,20 @@ static inline ProcessState LinuxProcessList_getProcessState(char state) {
}
}
-static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) {
- LinuxProcess* lp = (LinuxProcess*) process;
+static bool LinuxProcessTable_readStatFile(LinuxProcess* lp, openat_arg_t procFd, const LinuxMachine* lhost, bool scanMainThread, char* command, size_t commLen) {
+ Process* process = &lp->super;
char buf[MAX_READ + 1];
- ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf));
+ char path[22] = "stat";
+ if (scanMainThread) {
+ xSnprintf(path, sizeof(path), "task/%"PRIi32"/stat", (int32_t)Process_getPid(process));
+ }
+ ssize_t r = xReadfileat(procFd, path, buf, sizeof(buf));
if (r < 0)
return false;
/* (1) pid - %d */
- assert(process->pid == atoi(buf));
+ assert(Process_getPid(process) == atoi(buf));
char* location = strchr(buf, ' ');
if (!location)
return false;
@@ -391,11 +282,11 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location = end + 2;
/* (3) state - %c */
- process->state = LinuxProcessList_getProcessState(location[0]);
+ process->state = LinuxProcessTable_getProcessState(location[0]);
location += 2;
/* (4) ppid - %d */
- process->ppid = strtol(location, &location, 10);
+ Process_setParent(process, strtol(location, &location, 10));
location += 1;
/* (5) pgrp - %d */
@@ -435,19 +326,19 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location += 1;
/* (14) utime - %lu */
- lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
+ lp->utime = LinuxProcessTable_adjustTime(lhost, strtoull(location, &location, 10));
location += 1;
/* (15) stime - %lu */
- lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
+ lp->stime = LinuxProcessTable_adjustTime(lhost, strtoull(location, &location, 10));
location += 1;
/* (16) cutime - %ld */
- lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
+ lp->cutime = LinuxProcessTable_adjustTime(lhost, strtoull(location, &location, 10));
location += 1;
/* (17) cstime - %ld */
- lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
+ lp->cstime = LinuxProcessTable_adjustTime(lhost, strtoull(location, &location, 10));
location += 1;
/* (18) priority - %ld */
@@ -467,7 +358,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
/* (22) starttime - %llu */
if (process->starttime_ctime == 0) {
- process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100;
+ process->starttime_ctime = lhost->boottime + LinuxProcessTable_adjustTime(lhost, strtoll(location, &location, 10)) / 100;
} else {
location = strchr(location, ' ');
}
@@ -490,8 +381,90 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
return true;
}
+static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t procFd) {
+ LinuxProcess* lp = (LinuxProcess*) process;
+
+ unsigned long ctxt = 0;
+#ifdef HAVE_VSERVER
+ lp->vxid = 0;
+#endif
+
+ FILE* statusfile = fopenat(procFd, "status", "r");
+ if (!statusfile)
+ return false;
+
+ char buffer[PROC_LINE_LENGTH + 1];
+
+ while (fgets(buffer, sizeof(buffer), statusfile)) {
+
+ if (String_startsWith(buffer, "NSpid:")) {
+ const char* ptr = buffer;
+ int pid_ns_count = 0;
+ while (*ptr && *ptr != '\n' && !isdigit((unsigned char)*ptr))
+ ++ptr;
+
+ while (*ptr && *ptr != '\n') {
+ if (isdigit(*ptr))
+ pid_ns_count++;
+ while (isdigit((unsigned char)*ptr))
+ ++ptr;
+ while (*ptr && *ptr != '\n' && !isdigit((unsigned char)*ptr))
+ ++ptr;
+ }
+
+ if (pid_ns_count > 1)
+ process->isRunningInContainer = true;
+
+ } else if (String_startsWith(buffer, "CapPrm:")) {
+ char* ptr = buffer + strlen("CapPrm:");
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ uint64_t cap_permitted = fast_strtoull_hex(&ptr, 16);
+ process->elevated_priv = cap_permitted != 0 && process->st_uid != 0;
+
+ } else if (String_startsWith(buffer, "voluntary_ctxt_switches:")) {
+ unsigned long vctxt;
+ int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt);
+ if (ok >= 1) {
+ ctxt += vctxt;
+ }
+
+ } else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) {
+ unsigned long nvctxt;
+ int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt);
+ if (ok >= 1) {
+ ctxt += nvctxt;
+ }
+
+#ifdef HAVE_VSERVER
+ } else if (String_startsWith(buffer, "VxID:")) {
+ int vxid;
+ int ok = sscanf(buffer, "VxID:\t%32d", &vxid);
+ if (ok >= 1) {
+ lp->vxid = vxid;
+ }
+#ifdef HAVE_ANCIENT_VSERVER
+ } else if (String_startsWith(buffer, "s_context:")) {
+ int vxid;
+ int ok = sscanf(buffer, "s_context:\t%32d", &vxid);
+ if (ok >= 1) {
+ lp->vxid = vxid;
+ }
+#endif /* HAVE_ANCIENT_VSERVER */
+#endif /* HAVE_VSERVER */
+ }
+ }
+
+ fclose(statusfile);
+
+ lp->ctxt_diff = (ctxt > lp->ctxt_total) ? (ctxt - lp->ctxt_total) : 0;
+ lp->ctxt_total = ctxt;
+
+ return true;
+}
-static bool LinuxProcessList_updateUser(ProcessList* processList, Process* process, openat_arg_t procFd) {
+static bool LinuxProcessTable_updateUser(const Machine* host, Process* process, openat_arg_t procFd) {
struct stat sstat;
#ifdef HAVE_OPENAT
int statok = fstat(procFd, &sstat);
@@ -503,68 +476,77 @@ static bool LinuxProcessList_updateUser(ProcessList* processList, Process* proce
if (process->st_uid != sstat.st_uid) {
process->st_uid = sstat.st_uid;
- process->user = UsersTable_getRef(processList->usersTable, sstat.st_uid);
+ process->user = UsersTable_getRef(host->usersTable, sstat.st_uid);
}
return true;
}
-static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long realtimeMs) {
+static void LinuxProcessTable_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread) {
+ Process* process = &lp->super;
+ const Machine* host = process->super.host;
+ char path[20] = "io";
char buffer[1024];
- ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer));
+ if (scanMainThread) {
+ xSnprintf(path, sizeof(path), "task/%"PRIi32"/io", (int32_t)Process_getPid(process));
+ }
+ ssize_t r = xReadfileat(procFd, path, buffer, sizeof(buffer));
if (r < 0) {
- process->io_rate_read_bps = NAN;
- process->io_rate_write_bps = NAN;
- 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;
- process->io_last_scan_time_ms = realtimeMs;
+ lp->io_rate_read_bps = NAN;
+ lp->io_rate_write_bps = NAN;
+ lp->io_rchar = ULLONG_MAX;
+ lp->io_wchar = ULLONG_MAX;
+ lp->io_syscr = ULLONG_MAX;
+ lp->io_syscw = ULLONG_MAX;
+ lp->io_read_bytes = ULLONG_MAX;
+ lp->io_write_bytes = ULLONG_MAX;
+ lp->io_cancelled_write_bytes = ULLONG_MAX;
+ lp->io_last_scan_time_ms = host->realtimeMs;
return;
}
- unsigned long long last_read = process->io_read_bytes;
- unsigned long long last_write = process->io_write_bytes;
- unsigned long long time_delta = realtimeMs > process->io_last_scan_time_ms ? realtimeMs - process->io_last_scan_time_ms : 0;
+ unsigned long long last_read = lp->io_read_bytes;
+ unsigned long long last_write = lp->io_write_bytes;
+ unsigned long long time_delta = saturatingSub(host->realtimeMs, lp->io_last_scan_time_ms);
+
+ // Note: Linux Kernel documentation states that /proc/<pid>/io may be racy
+ // on 32-bit machines. (Documentation/filesystems/proc.rst)
char* buf = buffer;
const char* line;
while ((line = strsep(&buf, "\n")) != NULL) {
switch (line[0]) {
- case 'r':
- if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
- process->io_rchar = strtoull(line + 7, NULL, 10);
- } else if (String_startsWith(line + 1, "ead_bytes: ")) {
- process->io_read_bytes = strtoull(line + 12, NULL, 10);
- process->io_rate_read_bps = time_delta ? (process->io_read_bytes - last_read) * /*ms to s*/1000. / time_delta : NAN;
- }
- break;
- case 'w':
- if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
- process->io_wchar = strtoull(line + 7, NULL, 10);
- } else if (String_startsWith(line + 1, "rite_bytes: ")) {
- process->io_write_bytes = strtoull(line + 13, NULL, 10);
- process->io_rate_write_bps = time_delta ? (process->io_write_bytes - last_write) * /*ms to s*/1000. / time_delta : NAN;
- }
- break;
- case 's':
- if (line[4] == 'r' && String_startsWith(line + 1, "yscr: ")) {
- process->io_syscr = strtoull(line + 7, NULL, 10);
- } else if (String_startsWith(line + 1, "yscw: ")) {
- process->io_syscw = strtoull(line + 7, NULL, 10);
- }
- break;
- case 'c':
- if (String_startsWith(line + 1, "ancelled_write_bytes: ")) {
- process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10);
- }
+ case 'r':
+ if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
+ lp->io_rchar = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "ead_bytes: ")) {
+ lp->io_read_bytes = strtoull(line + 12, NULL, 10);
+ lp->io_rate_read_bps = time_delta ? saturatingSub(lp->io_read_bytes, last_read) * /*ms to s*/1000. / time_delta : NAN;
+ }
+ break;
+ case 'w':
+ if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
+ lp->io_wchar = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "rite_bytes: ")) {
+ lp->io_write_bytes = strtoull(line + 13, NULL, 10);
+ lp->io_rate_write_bps = time_delta ? saturatingSub(lp->io_write_bytes, last_write) * /*ms to s*/1000. / time_delta : NAN;
+ }
+ break;
+ case 's':
+ if (line[4] == 'r' && String_startsWith(line + 1, "yscr: ")) {
+ lp->io_syscr = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "yscw: ")) {
+ lp->io_syscw = strtoull(line + 7, NULL, 10);
+ }
+ break;
+ case 'c':
+ if (String_startsWith(line + 1, "ancelled_write_bytes: ")) {
+ lp->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10);
+ }
}
}
- process->io_last_scan_time_ms = realtimeMs;
+ lp->io_last_scan_time_ms = host->realtimeMs;
}
typedef struct LibraryData_ {
@@ -572,49 +554,7 @@ typedef struct LibraryData_ {
bool exec;
} LibraryData;
-static inline uint64_t fast_strtoull_dec(char** str, int maxlen) {
- register uint64_t result = 0;
-
- if (!maxlen)
- --maxlen;
-
- while (maxlen-- && **str >= '0' && **str <= '9') {
- result *= 10;
- result += **str - '0';
- (*str)++;
- }
-
- return result;
-}
-
-static inline uint64_t fast_strtoull_hex(char** str, int maxlen) {
- register uint64_t result = 0;
- register int nibble, letter;
- const long valid_mask = 0x03FF007E;
-
- if (!maxlen)
- --maxlen;
-
- while (maxlen--) {
- nibble = (unsigned char)**str;
- if (!(valid_mask & (1 << (nibble & 0x1F))))
- break;
- if ((nibble < '0') || (nibble & ~0x20) > 'F')
- break;
- letter = (nibble & 0x40) ? 'A' - '9' - 1 : 0;
- nibble &=~0x20; // to upper
- nibble ^= 0x10; // switch letters and digits
- nibble -= letter;
- nibble &= 0x0f;
- result <<= 4;
- result += (uint64_t)nibble;
- (*str)++;
- }
-
- return result;
-}
-
-static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* value, void* data) {
+static void LinuxProcessTable_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* value, void* data) {
if (!data)
return;
@@ -629,7 +569,7 @@ static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void*
*d += v->size;
}
-static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd, bool calcSize, bool checkDeletedLib) {
+static void LinuxProcessTable_readMaps(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host, bool calcSize, bool checkDeletedLib) {
Process* proc = (Process*)process;
proc->usesDeletedLib = false;
@@ -666,12 +606,15 @@ static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd
if (' ' != *readptr++)
continue;
+ if (!readptr[0] || !readptr[1] || !readptr[2] || !readptr[3])
+ continue;
+
map_execute = (readptr[2] == 'x');
readptr += 4;
if (' ' != *readptr++)
continue;
- while(*readptr > ' ')
+ while (*readptr > ' ')
readptr++; // Skip parsing this hex value
if (' ' != *readptr++)
continue;
@@ -730,15 +673,15 @@ static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd
if (calcSize) {
uint64_t total_size = 0;
- Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size);
+ Hashtable_foreach(ht, LinuxProcessTable_calcLibSize_helper, &total_size);
Hashtable_delete(ht);
- process->m_lrs = total_size / pageSize;
+ process->m_lrs = total_size / host->pageSize;
}
}
-static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd) {
+static bool LinuxProcessTable_readStatmFile(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host) {
FILE* statmfile = fopenat(procFd, "statm", "r");
if (!statmfile)
return false;
@@ -756,14 +699,16 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p
fclose(statmfile);
if (r == 7) {
- process->super.m_virt *= pageSizeKB;
- process->super.m_resident *= pageSizeKB;
+ process->super.m_virt *= host->pageSizeKB;
+ process->super.m_resident *= host->pageSizeKB;
+
+ process->m_priv = process->super.m_resident - (process->m_share * host->pageSizeKB);
}
return r == 7;
}
-static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, bool haveSmapsRollup) {
+static bool LinuxProcessTable_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, bool haveSmapsRollup) {
//http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719
//kernel will return data in chunks of size PAGE_SIZE or less.
FILE* f = fopenat(procFd, haveSmapsRollup ? "smaps_rollup" : "smaps", "r");
@@ -801,11 +746,11 @@ static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t p
#ifdef HAVE_OPENVZ
-static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t procFd) {
+static void LinuxProcessTable_readOpenVZData(LinuxProcess* process, openat_arg_t procFd) {
if (access(PROCDIR "/vz", R_OK) != 0) {
free(process->ctid);
process->ctid = NULL;
- process->vpid = process->super.pid;
+ process->vpid = Process_getPid(&process->super);
return;
}
@@ -813,7 +758,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t
if (!file) {
free(process->ctid);
process->ctid = NULL;
- process->vpid = process->super.pid;
+ process->vpid = Process_getPid(&process->super);
return;
}
@@ -851,7 +796,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t
char* value_end = name_value_sep;
- while(*value_end > 32) {
+ while (*value_end > 32) {
value_end++;
}
@@ -861,19 +806,19 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t
*value_end = '\0';
- switch(field) {
- case 1:
- foundEnvID = true;
- if (!String_eq(name_value_sep, process->ctid ? process->ctid : ""))
- free_and_xStrdup(&process->ctid, name_value_sep);
- break;
- case 2:
- foundVPid = true;
- process->vpid = strtoul(name_value_sep, NULL, 0);
- break;
- default:
- //Sanity Check: Should never reach here, or the implementation is missing something!
- assert(false && "OpenVZ handling: Unimplemented case for field handling reached.");
+ switch (field) {
+ case 1:
+ foundEnvID = true;
+ if (!String_eq(name_value_sep, process->ctid ? process->ctid : ""))
+ free_and_xStrdup(&process->ctid, name_value_sep);
+ break;
+ case 2:
+ foundVPid = true;
+ process->vpid = strtoul(name_value_sep, NULL, 0);
+ break;
+ default:
+ //Sanity Check: Should never reach here, or the implementation is missing something!
+ assert(false && "OpenVZ handling: Unimplemented case for field handling reached.");
}
}
@@ -885,13 +830,13 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t
}
if (!foundVPid) {
- process->vpid = process->super.pid;
+ process->vpid = Process_getPid(&process->super);
}
}
#endif
-static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) {
+static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) {
FILE* file = fopenat(procFd, "cgroup", "r");
if (!file) {
if (process->cgroup) {
@@ -902,6 +847,10 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
free(process->cgroup_short);
process->cgroup_short = NULL;
}
+ if (process->container_short) {
+ free(process->container_short);
+ process->container_short = NULL;
+ }
return;
}
char output[PROC_LINE_LENGTH + 1];
@@ -916,13 +865,13 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
char* group = buffer;
for (size_t i = 0; i < 2; i++) {
- group = strchrnul(group, ':');
+ group = String_strchrnul(group, ':');
if (!*group)
break;
group++;
}
- char* eol = strchrnul(group, '\n');
+ char* eol = String_strchrnul(group, '\n');
*eol = '\0';
if (at != output) {
@@ -937,65 +886,50 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
bool changed = !process->cgroup || !String_eq(process->cgroup, output);
- Process_updateFieldWidth(CGROUP, strlen(output));
+ Row_updateFieldWidth(CGROUP, strlen(output));
free_and_xStrdup(&process->cgroup, output);
if (!changed) {
- if(process->cgroup_short) {
- Process_updateFieldWidth(CCGROUP, strlen(process->cgroup_short));
+ if (process->cgroup_short) {
+ Row_updateFieldWidth(CCGROUP, strlen(process->cgroup_short));
} else {
//CCGROUP is alias to normal CGROUP if shortening fails
- Process_updateFieldWidth(CCGROUP, strlen(process->cgroup));
+ Row_updateFieldWidth(CCGROUP, strlen(process->cgroup));
+ }
+ if (process->container_short) {
+ Row_updateFieldWidth(CONTAINER, strlen(process->container_short));
+ } else {
+ Row_updateFieldWidth(CONTAINER, strlen("N/A"));
}
return;
}
char* cgroup_short = CGroup_filterName(process->cgroup);
if (cgroup_short) {
- Process_updateFieldWidth(CCGROUP, strlen(cgroup_short));
+ Row_updateFieldWidth(CCGROUP, strlen(cgroup_short));
free_and_xStrdup(&process->cgroup_short, cgroup_short);
free(cgroup_short);
} else {
//CCGROUP is alias to normal CGROUP if shortening fails
- Process_updateFieldWidth(CCGROUP, strlen(process->cgroup));
+ Row_updateFieldWidth(CCGROUP, strlen(process->cgroup));
free(process->cgroup_short);
process->cgroup_short = NULL;
}
-}
-
-#ifdef HAVE_VSERVER
-
-static void LinuxProcessList_readVServerData(LinuxProcess* process, openat_arg_t procFd) {
- FILE* file = fopenat(procFd, "status", "r");
- if (!file)
- return;
- char buffer[PROC_LINE_LENGTH + 1];
- process->vxid = 0;
- while (fgets(buffer, PROC_LINE_LENGTH, file)) {
- 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
+ char* container_short = CGroup_filterContainer(process->cgroup);
+ if (container_short) {
+ Row_updateFieldWidth(CONTAINER, strlen(container_short));
+ free_and_xStrdup(&process->container_short, container_short);
+ free(container_short);
+ } else {
+ //CONTAINER is just "N/A" if shortening fails
+ Row_updateFieldWidth(CONTAINER, strlen("N/A"));
+ free(process->container_short);
+ process->container_short = NULL;
}
- fclose(file);
}
-#endif
-
-static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t procFd) {
+static void LinuxProcessTable_readOomData(LinuxProcess* process, openat_arg_t procFd) {
FILE* file = fopenat(procFd, "oom_score", "r");
if (!file)
return;
@@ -1011,7 +945,7 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t pro
fclose(file);
}
-static void LinuxProcessList_readAutogroup(LinuxProcess* process, openat_arg_t procFd) {
+static void LinuxProcessTable_readAutogroup(LinuxProcess* process, openat_arg_t procFd) {
process->autogroup_id = -1;
char autogroup[64]; // space for two numeric values and fixed length strings
@@ -1028,34 +962,7 @@ static void LinuxProcessList_readAutogroup(LinuxProcess* process, openat_arg_t p
}
}
-static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) {
- FILE* file = fopenat(procFd, "status", "r");
- if (!file)
- return;
-
- char buffer[PROC_LINE_LENGTH + 1];
- unsigned long ctxt = 0;
- while (fgets(buffer, PROC_LINE_LENGTH, file)) {
- if (String_startsWith(buffer, "voluntary_ctxt_switches:")) {
- unsigned long vctxt;
- int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt);
- if (ok >= 1) {
- ctxt += vctxt;
- }
- } else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) {
- unsigned long nvctxt;
- int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt);
- if (ok >= 1) {
- ctxt += nvctxt;
- }
- }
- }
- fclose(file);
- process->ctxt_diff = (ctxt > process->ctxt_total) ? (ctxt - process->ctxt_total) : 0;
- process->ctxt_total = ctxt;
-}
-
-static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t procFd) {
+static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_t procFd) {
FILE* file = fopenat(procFd, "attr/current", "r");
if (!file) {
free(process->secattr);
@@ -1076,7 +983,7 @@ static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t
*newline = '\0';
}
- Process_updateFieldWidth(SECATTR, strlen(buffer));
+ Row_updateFieldWidth(SECATTR, strlen(buffer));
if (process->secattr && String_eq(process->secattr, buffer)) {
return;
@@ -1084,15 +991,13 @@ static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t
free_and_xStrdup(&process->secattr, buffer);
}
-static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) {
+static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd) {
char pathBuffer[PATH_MAX + 1] = {0};
#if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT)
ssize_t r = readlinkat(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1);
#else
- char filename[MAX_NAME + 1];
- xSnprintf(filename, sizeof(filename), "%s/cwd", procFd);
- ssize_t r = readlink(filename, pathBuffer, sizeof(pathBuffer) - 1);
+ ssize_t r = Compat_readlink(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1);
#endif
if (r < 0) {
@@ -1127,16 +1032,16 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats));
- assert(lp->super.pid == (pid_t)stats.ac_pid);
+ assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid);
+ // The xxx_delay_total values wrap around on overflow.
+ // (Linux Kernel "Documentation/accounting/taskstats-struct.rst")
unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
- #define BOUNDS(x) (isnan(x) ? 0.0 : ((x) > 100) ? 100.0 : (x))
- #define DELTAPERC(x,y) BOUNDS((float) ((x) - (y)) / timeDelta * 100)
+ #define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0f, 100.0f) : NAN)
lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total);
lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total);
lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC
- #undef BOUNDS
lp->swapin_delay_total = stats.swapin_delay_total;
lp->blkio_delay_total = stats.blkio_delay_total;
@@ -1146,11 +1051,11 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
return NL_OK;
}
-static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
+static void LinuxProcessTable_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) {
struct nl_msg* msg;
if (!this->netlink_socket) {
- LinuxProcessList_initNetlinkSocket(this);
+ LinuxProcessTable_initNetlinkSocket(this);
if (!this->netlink_socket) {
goto delayacct_failure;
}
@@ -1168,7 +1073,7 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
nlmsg_free(msg);
}
- if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) {
+ if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) {
nlmsg_free(msg);
}
@@ -1190,7 +1095,7 @@ delayacct_failure:
#endif
-static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) {
+static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t procFd) {
char command[4096 + 1]; // max cmdline length on Linux
ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command));
if (amtRead <= 0)
@@ -1329,9 +1234,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
#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);
+ amtRead = Compat_readlink(procFd, "exe", filename, sizeof(filename) - 1);
#endif
if (amtRead > 0) {
filename[amtRead] = 0;
@@ -1365,7 +1268,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
return true;
}
-static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned long int tty_nr) {
+static char* LinuxProcessTable_updateTtyDevice(TtyDriver* ttyDrivers, unsigned long int tty_nr) {
unsigned int maj = major(tty_nr);
unsigned int min = minor(tty_nr);
@@ -1418,14 +1321,16 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo
return out;
}
-static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int seconds) {
- assert(pl->realtimeMs > 0);
+static bool isOlderThan(const Process* proc, unsigned int seconds) {
+ const Machine* host = proc->super.host;
+
+ assert(host->realtimeMs > 0);
/* Starttime might not yet be parsed */
if (proc->starttime_ctime <= 0)
- return false;
+ return false;
- uint64_t realtime = pl->realtimeMs / 1000;
+ uint64_t realtime = host->realtimeMs / 1000;
if (realtime < (uint64_t)proc->starttime_ctime)
return false;
@@ -1433,11 +1338,15 @@ static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int
return realtime - proc->starttime_ctime > seconds;
}
-static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) {
- ProcessList* pl = (ProcessList*) this;
- const struct dirent* entry;
- const Settings* settings = pl->settings;
+static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_arg_t parentFd, const LinuxMachine* lhost, const char* dirname, const Process* parent) {
+ ProcessTable* pt = (ProcessTable*) this;
+ const Machine* host = &lhost->super;
+ const Settings* settings = host->settings;
const ScreenSettings* ss = settings->ss;
+ const struct dirent* entry;
+
+ /* set runningTasks from /proc/stat (from Machine_scanCPUTime) */
+ pt->runningTasks = lhost->runningTasks;
#ifdef HAVE_OPENAT
int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
@@ -1454,9 +1363,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
return false;
}
- const unsigned int activeCPUs = pl->activeCPUs;
const bool hideKernelThreads = settings->hideKernelThreads;
const bool hideUserlandThreads = settings->hideUserlandThreads;
+ const bool hideRunningInContainer = settings->hideRunningInContainer;
while ((entry = readdir(dir)) != NULL) {
const char* name = entry->d_name;
@@ -1487,7 +1396,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
// Skip task directory of main thread
- if (parent && pid == parent->pid)
+ if (parent && pid == Process_getPid(parent))
continue;
#ifdef HAVE_OPENAT
@@ -1500,57 +1409,64 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
#endif
bool preExisting;
- Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
+ Process* proc = ProcessTable_getProcess(pt, pid, &preExisting, LinuxProcess_new);
LinuxProcess* lp = (LinuxProcess*) proc;
- proc->tgid = parent ? parent->pid : pid;
- proc->isUserlandThread = proc->pid != proc->tgid;
+ Process_setThreadGroup(proc, parent ? Process_getPid(parent) : pid);
+ proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc);
- LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period);
+ LinuxProcessTable_recurseProcTree(this, procFd, lhost, "task", proc);
/*
* These conditions will not trigger on first occurrence, cause we need to
- * add the process to the ProcessList and do all one time scans
+ * add the process to the ProcessTable 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++;
+ proc->super.updated = true;
+ proc->super.show = false;
+ pt->kernelThreads++;
+ pt->totalTasks++;
Compat_openatArgClose(procFd);
continue;
}
if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
- proc->updated = true;
- proc->show = false;
- pl->userlandThreads++;
- pl->totalTasks++;
+ proc->super.updated = true;
+ proc->super.show = false;
+ pt->userlandThreads++;
+ pt->totalTasks++;
+ Compat_openatArgClose(procFd);
+ continue;
+ }
+ if (preExisting && hideRunningInContainer && proc->isRunningInContainer) {
+ proc->super.updated = true;
+ proc->super.show = false;
Compat_openatArgClose(procFd);
continue;
}
+ bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !parent;
if (ss->flags & PROCESS_FLAG_IO)
- LinuxProcessList_readIoFile(lp, procFd, pl->realtimeMs);
+ LinuxProcessTable_readIoFile(lp, procFd, scanMainThread);
- if (!LinuxProcessList_readStatmFile(lp, procFd))
+ if (!LinuxProcessTable_readStatmFile(lp, procFd, lhost))
goto errorReadingProcess;
{
bool prev = proc->usesDeletedLib;
if (!proc->isKernelThread && !proc->isUserlandThread &&
- ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(pl, proc, 10)))) {
+ ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(proc, 10)))) {
// Check if we really should recalculate the M_LRS value for this process
- uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime;
+ uint64_t passedTimeInMs = host->realtimeMs - lp->last_mlrs_calctime;
uint64_t recheck = ((uint64_t)rand()) % 2048;
if (passedTimeInMs > recheck) {
- lp->last_mlrs_calctime = pl->realtimeMs;
- LinuxProcessList_readMaps(lp, procFd, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe);
+ lp->last_mlrs_calctime = host->realtimeMs;
+ LinuxProcessTable_readMaps(lp, procFd, lhost, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe);
}
} else {
/* Copy from process structure in threads and reset if setting got disabled */
@@ -1567,7 +1483,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
// Read smaps file of each process only every second pass to improve performance
static int smaps_flag = 0;
if ((pid & 1) == smaps_flag) {
- LinuxProcessList_readSmapsFile(lp, procFd, this->haveSmapsRollup);
+ LinuxProcessTable_readSmapsFile(lp, procFd, this->haveSmapsRollup);
}
if (pid == 1) {
smaps_flag = !smaps_flag;
@@ -1580,7 +1496,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
char statCommand[MAX_NAME + 1];
unsigned long long int lasttimes = (lp->utime + lp->stime);
unsigned long int tty_nr = proc->tty_nr;
- if (! LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand)))
+ if (!LinuxProcessTable_readStatFile(lp, procFd, lhost, scanMainThread, statCommand, sizeof(statCommand)))
goto errorReadingProcess;
if (lp->flags & PF_KTHREAD) {
@@ -1589,103 +1505,114 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
free(proc->tty_name);
- proc->tty_name = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
+ proc->tty_name = LinuxProcessTable_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) {
- LinuxProcess_updateIOPriority(lp);
+ LinuxProcess_updateIOPriority(proc);
}
- /* period might be 0 after system sleep */
- float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
- proc->percent_cpu = CLAMP(percent_cpu, 0.0F, activeCPUs * 100.0F);
- proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0;
+ proc->percent_cpu = NAN;
+ /* lhost->period might be 0 after system sleep */
+ if (lhost->period > 0.0) {
+ float percent_cpu = saturatingSub(lp->utime + lp->stime, lasttimes) / lhost->period * 100.0;
+ proc->percent_cpu = MINIMUM(percent_cpu, host->activeCPUs * 100.0F);
+ }
+ proc->percent_mem = proc->m_resident / (double)(host->totalMem) * 100.0;
Process_updateCPUFieldWidths(proc->percent_cpu);
- if (! LinuxProcessList_updateUser(pl, proc, procFd))
+ if (!LinuxProcessTable_updateUser(host, proc, procFd))
+ goto errorReadingProcess;
+
+ if (!LinuxProcessTable_readStatusFile(proc, procFd))
goto errorReadingProcess;
if (!preExisting) {
#ifdef HAVE_OPENVZ
if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) {
- LinuxProcessList_readOpenVZData(lp, procFd);
- }
- #endif
-
- #ifdef HAVE_VSERVER
- if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) {
- LinuxProcessList_readVServerData(lp, procFd);
+ LinuxProcessTable_readOpenVZData(lp, procFd);
}
#endif
if (proc->isKernelThread) {
Process_updateCmdline(proc, NULL, 0, 0);
- } else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
+ } else if (!LinuxProcessTable_readCmdlineFile(proc, procFd)) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
Process_fillStarttimeBuffer(proc);
- ProcessList_add(pl, proc);
+ ProcessTable_add(pt, proc);
} else {
if (settings->updateProcessNames && proc->state != ZOMBIE) {
if (proc->isKernelThread) {
Process_updateCmdline(proc, NULL, 0, 0);
- } else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
+ } else if (!LinuxProcessTable_readCmdlineFile(proc, procFd)) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
}
}
+ if (ss->flags & PROCESS_FLAG_LINUX_CGROUP)
+ LinuxProcessTable_readCGroupFile(lp, procFd);
+
#ifdef HAVE_DELAYACCT
if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) {
- LinuxProcessList_readDelayAcctData(this, lp);
+ LinuxProcessTable_readDelayAcctData(this, lp);
}
#endif
- if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) {
- LinuxProcessList_readCGroupFile(lp, procFd);
- }
-
if (ss->flags & PROCESS_FLAG_LINUX_OOM) {
- LinuxProcessList_readOomData(lp, procFd);
- }
-
- if (ss->flags & PROCESS_FLAG_LINUX_CTXT) {
- LinuxProcessList_readCtxtData(lp, procFd);
+ LinuxProcessTable_readOomData(lp, procFd);
}
if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) {
- LinuxProcessList_readSecattrData(lp, procFd);
+ LinuxProcessTable_readSecattrData(lp, procFd);
}
if (ss->flags & PROCESS_FLAG_CWD) {
- LinuxProcessList_readCwd(lp, procFd);
+ LinuxProcessTable_readCwd(lp, procFd);
}
if ((ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) {
- LinuxProcessList_readAutogroup(lp, procFd);
+ LinuxProcessTable_readAutogroup(lp, procFd);
}
+ #ifdef SCHEDULER_SUPPORT
+ if (ss->flags & PROCESS_FLAG_SCHEDPOL) {
+ Scheduling_readProcessPolicy(proc);
+ }
+ #endif
+
if (!proc->cmdline && statCommand[0] &&
(proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
+ /*
+ * Final section after all data has been gathered
+ */
+
+ proc->super.updated = true;
+ Compat_openatArgClose(procFd);
+
+ if (hideRunningInContainer && proc->isRunningInContainer) {
+ proc->super.show = false;
+ continue;
+ }
+
if (Process_isKernelThread(proc)) {
- pl->kernelThreads++;
+ pt->kernelThreads++;
} else if (Process_isUserlandThread(proc)) {
- pl->userlandThreads++;
+ pt->userlandThreads++;
}
/* Set at the end when we know if a new entry is a thread */
- proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
+ proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
- pl->totalTasks++;
- /* runningTasks is set in LinuxProcessList_scanCPUTime() from /proc/stat */
- proc->updated = true;
- Compat_openatArgClose(procFd);
+ pt->totalTasks++;
+ /* runningTasks is set in Machine_scanCPUTime() from /proc/stat */
continue;
// Exception handler.
@@ -1713,512 +1640,11 @@ errorReadingProcess:
return true;
}
-static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
- memory_t availableMem = 0;
- memory_t freeMem = 0;
- memory_t totalMem = 0;
- memory_t buffersMem = 0;
- memory_t cachedMem = 0;
- memory_t sharedMem = 0;
- memory_t swapTotalMem = 0;
- memory_t swapCacheMem = 0;
- memory_t swapFreeMem = 0;
- memory_t sreclaimableMem = 0;
-
- FILE* file = fopen(PROCMEMINFOFILE, "r");
- if (!file)
- CRT_fatalError("Cannot open " PROCMEMINFOFILE);
-
- char buffer[128];
- while (fgets(buffer, sizeof(buffer), file)) {
-
- #define tryRead(label, variable) \
- if (String_startsWith(buffer, label)) { \
- memory_t parsed_; \
- if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \
- (variable) = parsed_; \
- } \
- break; \
- } else (void) 0 /* Require a ";" after the macro use. */
-
- switch (buffer[0]) {
- case 'M':
- tryRead("MemAvailable:", availableMem);
- tryRead("MemFree:", freeMem);
- tryRead("MemTotal:", totalMem);
- break;
- case 'B':
- tryRead("Buffers:", buffersMem);
- break;
- case 'C':
- tryRead("Cached:", cachedMem);
- break;
- case 'S':
- switch (buffer[1]) {
- case 'h':
- tryRead("Shmem:", sharedMem);
- break;
- case 'w':
- tryRead("SwapTotal:", swapTotalMem);
- tryRead("SwapCached:", swapCacheMem);
- tryRead("SwapFree:", swapFreeMem);
- break;
- case 'R':
- tryRead("SReclaimable:", sreclaimableMem);
- break;
- }
- break;
- }
-
- #undef tryRead
- }
-
- fclose(file);
-
- /*
- * Compute memory partition like procps(free)
- * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c
- *
- * 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.
- */
- this->totalMem = totalMem;
- this->cachedMem = cachedMem + sreclaimableMem - sharedMem;
- this->sharedMem = sharedMem;
- const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem;
- 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;
-}
-
-static void LinuxProcessList_scanHugePages(LinuxProcessList* this) {
- this->totalHugePageMem = 0;
- for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) {
- this->usedHugePageMem[i] = MEMORY_MAX;
- }
-
- 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;
-
- memory_t total = strtoull(content, NULL, 10);
- 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;
-
- memory_t free = strtoull(content, NULL, 10);
-
- int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10);
- assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT);
-
- this->totalHugePageMem += total * hugePageSize;
- this->usedHugePageMem[shift] = (total - free) * hugePageSize;
- }
-
- closedir(dir);
-}
-
-static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) {
- memory_t totalZram = 0;
- memory_t usedZramComp = 0;
- memory_t usedZramOrig = 0;
-
- char mm_stat[34];
- char disksize[34];
-
- unsigned int i = 0;
- for (;;) {
- xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i);
- xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i);
- i++;
- FILE* disksize_file = fopen(disksize, "r");
- FILE* mm_stat_file = fopen(mm_stat, "r");
- if (disksize_file == NULL || mm_stat_file == NULL) {
- if (disksize_file) {
- fclose(disksize_file);
- }
- if (mm_stat_file) {
- fclose(mm_stat_file);
- }
- break;
- }
- memory_t size = 0;
- memory_t orig_data_size = 0;
- memory_t compr_data_size = 0;
-
- if (!fscanf(disksize_file, "%llu\n", &size) ||
- !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) {
- fclose(disksize_file);
- fclose(mm_stat_file);
- break;
- }
-
- totalZram += size;
- usedZramComp += compr_data_size;
- usedZramOrig += orig_data_size;
-
- fclose(disksize_file);
- fclose(mm_stat_file);
- }
-
- this->zram.totalZram = totalZram / 1024;
- this->zram.usedZramComp = usedZramComp / 1024;
- this->zram.usedZramOrig = usedZramOrig / 1024;
-}
-
-static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
- memory_t dbufSize = 0;
- memory_t dnodeSize = 0;
- memory_t bonusSize = 0;
-
- FILE* file = fopen(PROCARCSTATSFILE, "r");
- if (file == NULL) {
- lpl->zfs.enabled = 0;
- return;
- }
- char buffer[128];
- while (fgets(buffer, 128, file)) {
- #define tryRead(label, variable) \
- if (String_startsWith(buffer, label)) { \
- sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
- break; \
- } else (void) 0 /* Require a ";" after the macro use. */
- #define tryReadFlag(label, variable, flag) \
- if (String_startsWith(buffer, label)) { \
- (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
- break; \
- } else (void) 0 /* Require a ";" after the macro use. */
-
- switch (buffer[0]) {
- case 'c':
- tryRead("c_min", &lpl->zfs.min);
- tryRead("c_max", &lpl->zfs.max);
- tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed);
- break;
- case 'u':
- tryRead("uncompressed_size", &lpl->zfs.uncompressed);
- break;
- case 's':
- tryRead("size", &lpl->zfs.size);
- break;
- case 'h':
- tryRead("hdr_size", &lpl->zfs.header);
- break;
- case 'd':
- tryRead("dbuf_size", &dbufSize);
- tryRead("dnode_size", &dnodeSize);
- break;
- case 'b':
- tryRead("bonus_size", &bonusSize);
- break;
- case 'a':
- tryRead("anon_size", &lpl->zfs.anon);
- break;
- case 'm':
- tryRead("mfu_size", &lpl->zfs.MFU);
- tryRead("mru_size", &lpl->zfs.MRU);
- break;
- }
- #undef tryRead
- #undef tryReadFlag
- }
- fclose(file);
-
- lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0);
- lpl->zfs.size /= 1024;
- lpl->zfs.min /= 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;
- if ( lpl->zfs.isCompressed ) {
- lpl->zfs.compressed /= 1024;
- lpl->zfs.uncompressed /= 1024;
- }
-}
-
-static inline double LinuxProcessList_scanCPUTime(ProcessList* super) {
- LinuxProcessList* this = (LinuxProcessList*) super;
-
- LinuxProcessList_updateCPUcount(super);
-
- FILE* file = fopen(PROCSTATFILE, "r");
- if (!file)
- CRT_fatalError("Cannot open " PROCSTATFILE);
-
- unsigned int existingCPUs = super->existingCPUs;
- unsigned int lastAdjCpuId = 0;
-
- for (unsigned int i = 0; i <= existingCPUs; i++) {
- char buffer[PROC_LINE_LENGTH + 1];
- unsigned long long int usertime, nicetime, systemtime, idletime;
- unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0;
-
- const char* ok = fgets(buffer, sizeof(buffer), file);
- if (!ok)
- break;
-
- // 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;
- if (i == 0) {
- (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
- adjCpuId = 0;
- } else {
- 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);
- 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));
- }
- lastAdjCpuId = adjCpuId;
-
- // Guest time is already accounted in usertime
- usertime -= guest;
- nicetime -= guestnice;
- // Fields existing on kernels >= 2.6
- // (and RHEL's patched kernel 2.4...)
- 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;
- CPUData* cpuData = &(this->cpuData[adjCpuId]);
- // 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.
- 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);
- 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;
- }
-
- double period = (double)this->cpuData[0].totalPeriod / super->activeCPUs;
-
- 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;
- }
- }
-
- fclose(file);
-
- return period;
-}
-
-static int scanCPUFrequencyFromSysCPUFreq(LinuxProcessList* this) {
- unsigned int existingCPUs = this->super.existingCPUs;
- int numCPUsWithFrequency = 0;
- unsigned long totalFrequency = 0;
-
- /*
- * 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;
- }
-
- for (unsigned int i = 0; i < existingCPUs; ++i) {
- if (!ProcessList_isCPUonline(&this->super, i))
- continue;
-
- char pathBuffer[64];
- xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
-
- struct timespec start;
- if (i == 0)
- clock_gettime(CLOCK_MONOTONIC, &start);
-
- FILE* file = fopen(pathBuffer, "r");
- if (!file)
- return -errno;
-
- unsigned long frequency;
- if (fscanf(file, "%lu", &frequency) == 1) {
- /* convert kHz to MHz */
- frequency = frequency / 1000;
- this->cpuData[i + 1].frequency = frequency;
- numCPUsWithFrequency++;
- totalFrequency += frequency;
- }
-
- fclose(file);
-
- 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;
- }
- }
-
- }
-
- if (numCPUsWithFrequency > 0)
- this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
-
- return 0;
-}
-
-static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) {
- FILE* file = fopen(PROCCPUINFOFILE, "r");
- if (file == NULL)
- return;
-
- unsigned int existingCPUs = this->super.existingCPUs;
- int numCPUsWithFrequency = 0;
- double totalFrequency = 0;
- int cpuid = -1;
-
- while (!feof(file)) {
- double frequency;
- char buffer[PROC_LINE_LENGTH];
-
- if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
- break;
-
- if (sscanf(buffer, "processor : %d", &cpuid) == 1) {
- continue;
- } else if (
- (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
- (sscanf(buffer, "clock : %lfMHz", &frequency) == 1)
- ) {
- if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) {
- continue;
- }
-
- CPUData* cpuData = &(this->cpuData[cpuid + 1]);
- /* do not override sysfs data */
- if (isnan(cpuData->frequency)) {
- cpuData->frequency = frequency;
- }
- numCPUsWithFrequency++;
- totalFrequency += frequency;
- } else if (buffer[0] == '\n') {
- cpuid = -1;
- }
- }
- fclose(file);
-
- if (numCPUsWithFrequency > 0) {
- this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency;
- }
-}
-
-static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
- unsigned int existingCPUs = this->super.existingCPUs;
-
- for (unsigned int i = 0; i <= existingCPUs; i++) {
- this->cpuData[i].frequency = NAN;
- }
-
- if (scanCPUFrequencyFromSysCPUFreq(this) == 0) {
- return;
- }
-
- scanCPUFrequencyFromCPUinfo(this);
-}
-
-void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
- LinuxProcessList* this = (LinuxProcessList*) super;
- const Settings* settings = super->settings;
-
- LinuxProcessList_scanMemoryInfo(super);
- LinuxProcessList_scanHugePages(this);
- LinuxProcessList_scanZfsArcstats(this);
- LinuxProcessList_scanZramInfo(this);
-
- double period = LinuxProcessList_scanCPUTime(super);
-
- if (settings->showCPUFrequency) {
- LinuxProcessList_scanCPUFrequency(this);
- }
-
- #ifdef HAVE_SENSORS_SENSORS_H
- if (settings->showCPUTemperature)
- LibSensors_getCPUTemperatures(this->cpuData, this->super.existingCPUs, this->super.activeCPUs);
- #endif
-
- // in pause mode only gather global data for meters (CPU/memory/...)
- if (pauseProcessUpdate) {
- return;
- }
+void ProcessTable_goThroughEntries(ProcessTable* super) {
+ LinuxProcessTable* this = (LinuxProcessTable*) super;
+ const Machine* host = super->super.host;
+ const Settings* settings = host->settings;
+ const LinuxMachine* lhost = (const LinuxMachine*) host;
if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) {
// Refer to sched(7) 'autogroup feature' section
@@ -2238,12 +1664,5 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
openat_arg_t rootFd = "";
#endif
- LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period);
-}
-
-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;
+ LinuxProcessTable_recurseProcTree(this, rootFd, lhost, PROCDIR, NULL);
}
diff --git a/linux/LinuxProcessTable.h b/linux/LinuxProcessTable.h
new file mode 100644
index 0000000..b87f9b0
--- /dev/null
+++ b/linux/LinuxProcessTable.h
@@ -0,0 +1,35 @@
+#ifndef HEADER_LinuxProcessTable
+#define HEADER_LinuxProcessTable
+/*
+htop - LinuxProcessTable.h
+(C) 2014 Hisham H. Muhammad
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include <stdbool.h>
+
+#include "ProcessTable.h"
+
+
+typedef struct TtyDriver_ {
+ char* path;
+ unsigned int major;
+ unsigned int minorFrom;
+ unsigned int minorTo;
+} TtyDriver;
+
+typedef struct LinuxProcessTable_ {
+ ProcessTable super;
+
+ TtyDriver* ttyDrivers;
+ bool haveSmapsRollup;
+ bool haveAutogroup;
+
+ #ifdef HAVE_DELAYACCT
+ struct nl_sock* netlink_socket;
+ int netlink_family;
+ #endif
+} LinuxProcessTable;
+
+#endif
diff --git a/linux/Platform.c b/linux/Platform.c
index 38b66e8..8dc8bb5 100644
--- a/linux/Platform.c
+++ b/linux/Platform.c
@@ -5,13 +5,13 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h"
+#include "config.h" // IWYU pragma: keep
#include "linux/Platform.h"
#include <assert.h>
-#include <ctype.h>
#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <math.h>
@@ -21,6 +21,7 @@ in the source distribution for its full text.
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <sys/sysmacros.h>
#include "BatteryMeter.h"
#include "ClockMeter.h"
@@ -29,9 +30,11 @@ in the source distribution for its full text.
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "DiskIOMeter.h"
+#include "FileDescriptorMeter.h"
#include "HostnameMeter.h"
#include "HugePageMeter.h"
#include "LoadAverageMeter.h"
+#include "Machine.h"
#include "Macros.h"
#include "MainPanel.h"
#include "Meter.h"
@@ -41,9 +44,7 @@ in the source distribution for its full text.
#include "Object.h"
#include "Panel.h"
#include "PressureStallMeter.h"
-#include "ProcessList.h"
#include "ProvideCurses.h"
-#include "linux/SELinuxMeter.h"
#include "Settings.h"
#include "SwapMeter.h"
#include "SysArchMeter.h"
@@ -52,17 +53,18 @@ in the source distribution for its full text.
#include "XUtils.h"
#include "linux/IOPriority.h"
#include "linux/IOPriorityPanel.h"
+#include "linux/LinuxMachine.h"
#include "linux/LinuxProcess.h"
-#include "linux/LinuxProcessList.h"
+#include "linux/SELinuxMeter.h"
#include "linux/SystemdMeter.h"
#include "linux/ZramMeter.h"
#include "linux/ZramStats.h"
+#include "linux/ZswapStats.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/ZfsCompressedArcMeter.h"
#ifdef HAVE_LIBCAP
-#include <errno.h>
#include <sys/capability.h>
#endif
@@ -161,7 +163,7 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) {
const void* set = Action_pickFromVector(st, ioprioPanel, 20, true);
if (set) {
IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel);
- bool ok = MainPanel_foreachProcess(st->mainPanel, LinuxProcess_setIOPriority, (Arg) { .i = ioprio2 }, NULL);
+ bool ok = MainPanel_foreachRow(st->mainPanel, LinuxProcess_rowSetIOPriority, (Arg) { .i = ioprio2 }, NULL);
if (!ok) {
beep();
}
@@ -176,7 +178,7 @@ static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) {
return false;
}
bool anyTagged;
- bool ok = MainPanel_foreachProcess(panel, LinuxProcess_changeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged);
+ bool ok = MainPanel_foreachRow(panel, LinuxProcess_rowChangeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged);
if (!ok)
beep();
return anyTagged;
@@ -238,6 +240,7 @@ const MeterClass* const Platform_meterTypes[] = {
&PressureStallCPUSomeMeter_class,
&PressureStallIOSomeMeter_class,
&PressureStallIOFullMeter_class,
+ &PressureStallIRQFullMeter_class,
&PressureStallMemorySomeMeter_class,
&PressureStallMemoryFullMeter_class,
&ZfsArcMeter_class,
@@ -247,10 +250,12 @@ const MeterClass* const Platform_meterTypes[] = {
&NetworkIOMeter_class,
&SELinuxMeter_class,
&SystemdMeter_class,
+ &SystemdUserMeter_class,
+ &FileDescriptorMeter_class,
NULL
};
-int Platform_getUptime() {
+int Platform_getUptime(void) {
double uptime = 0;
FILE* fd = fopen(PROCDIR "/uptime", "r");
if (fd) {
@@ -285,12 +290,12 @@ err:
*fifteen = NAN;
}
-int Platform_getMaxPid() {
+pid_t Platform_getMaxPid(void) {
+ pid_t maxPid = 4194303;
FILE* file = fopen(PROCDIR "/sys/kernel/pid_max", "r");
if (!file)
- return -1;
+ return maxPid;
- int maxPid = 4194303;
int match = fscanf(file, "%32d", &maxPid);
(void) match;
fclose(file);
@@ -298,8 +303,9 @@ int Platform_getMaxPid() {
}
double Platform_setCPUValues(Meter* this, unsigned int cpu) {
- const LinuxProcessList* pl = (const LinuxProcessList*) this->pl;
- const CPUData* cpuData = &(pl->cpuData[cpu]);
+ const LinuxMachine* lhost = (const LinuxMachine*) this->host;
+ const Settings* settings = this->host->settings;
+ const CPUData* cpuData = &(lhost->cpuData[cpu]);
double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod);
double percent;
double* v = this->values;
@@ -311,28 +317,30 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) {
v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
- if (this->pl->settings->detailedCPUTime) {
+ if (settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0;
v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0;
v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0;
+ this->curItems = 5;
+
v[CPU_METER_STEAL] = cpuData->stealPeriod / total * 100.0;
v[CPU_METER_GUEST] = cpuData->guestPeriod / total * 100.0;
- v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0;
- this->curItems = 8;
- if (this->pl->settings->accountGuestInCPUMeter) {
- percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6];
- } else {
- percent = v[0] + v[1] + v[2] + v[3] + v[4];
+ if (settings->accountGuestInCPUMeter) {
+ this->curItems = 7;
}
+
+ v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0;
} else {
- v[2] = cpuData->systemAllPeriod / total * 100.0;
- v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
+ v[CPU_METER_KERNEL] = cpuData->systemAllPeriod / total * 100.0;
+ v[CPU_METER_IRQ] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
this->curItems = 4;
- percent = v[0] + v[1] + v[2] + v[3];
}
- percent = CLAMP(percent, 0.0, 100.0);
- if (isnan(percent)) {
- percent = 0.0;
+
+ percent = sumPositiveValues(v, this->curItems);
+ percent = MINIMUM(percent, 100.0);
+
+ if (settings->detailedCPUTime) {
+ this->curItems = 8;
}
v[CPU_METER_FREQUENCY] = cpuData->frequency;
@@ -347,51 +355,81 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) {
}
void Platform_setMemoryValues(Meter* this) {
- const ProcessList* pl = this->pl;
- const LinuxProcessList* lpl = (const LinuxProcessList*) pl;
-
- this->total = pl->totalMem;
- this->values[0] = pl->usedMem;
- this->values[1] = pl->buffersMem;
- this->values[2] = pl->sharedMem;
- this->values[3] = pl->cachedMem;
- this->values[4] = pl->availableMem;
-
- if (lpl->zfs.enabled != 0 && !Running_containerized) {
+ const Machine* host = this->host;
+ const LinuxMachine* lhost = (const LinuxMachine*) host;
+
+ this->total = host->totalMem;
+ this->values[MEMORY_METER_USED] = host->usedMem;
+ this->values[MEMORY_METER_SHARED] = host->sharedMem;
+ this->values[MEMORY_METER_COMPRESSED] = 0; /* compressed */
+ this->values[MEMORY_METER_BUFFERS] = host->buffersMem;
+ this->values[MEMORY_METER_CACHE] = host->cachedMem;
+ this->values[MEMORY_METER_AVAILABLE] = host->availableMem;
+
+ if (lhost->zfs.enabled != 0 && !Running_containerized) {
// ZFS does not shrink below the value of zfs_arc_min.
unsigned long long int shrinkableSize = 0;
- if (lpl->zfs.size > lpl->zfs.min)
- shrinkableSize = lpl->zfs.size - lpl->zfs.min;
- this->values[0] -= shrinkableSize;
- this->values[3] += shrinkableSize;
- this->values[4] += shrinkableSize;
+ if (lhost->zfs.size > lhost->zfs.min)
+ shrinkableSize = lhost->zfs.size - lhost->zfs.min;
+ this->values[MEMORY_METER_USED] -= shrinkableSize;
+ this->values[MEMORY_METER_CACHE] += shrinkableSize;
+ this->values[MEMORY_METER_AVAILABLE] += shrinkableSize;
+ }
+
+ if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) {
+ this->values[MEMORY_METER_USED] -= lhost->zswap.usedZswapComp;
+ this->values[MEMORY_METER_COMPRESSED] += lhost->zswap.usedZswapComp;
}
}
void Platform_setSwapValues(Meter* this) {
- const ProcessList* pl = this->pl;
- this->total = pl->totalSwap;
- this->values[0] = pl->usedSwap;
- this->values[1] = pl->cachedSwap;
+ const Machine* host = this->host;
+ const LinuxMachine* lhost = (const LinuxMachine*) host;
+
+ this->total = host->totalSwap;
+ this->values[SWAP_METER_USED] = host->usedSwap;
+ this->values[SWAP_METER_CACHE] = host->cachedSwap;
+ this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */
+
+ if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) {
+ /*
+ * FIXME: Zswapped pages can be both SwapUsed and SwapCached, and we do not know which.
+ *
+ * Apparently, it is possible that Zswapped > SwapUsed. This means that some of Zswapped pages
+ * were actually SwapCached, nor SwapUsed. Unfortunately, we cannot tell what exactly portion
+ * of Zswapped pages were SwapCached.
+ *
+ * For now, subtract Zswapped from SwapUsed and only if Zswapped > SwapUsed, subtract the
+ * overflow from SwapCached.
+ */
+ this->values[SWAP_METER_USED] -= lhost->zswap.usedZswapOrig;
+ if (this->values[SWAP_METER_USED] < 0) {
+ /* subtract the overflow from SwapCached */
+ this->values[SWAP_METER_CACHE] += this->values[SWAP_METER_USED];
+ this->values[SWAP_METER_USED] = 0;
+ }
+ this->values[SWAP_METER_FRONTSWAP] += lhost->zswap.usedZswapOrig;
+ }
}
void Platform_setZramValues(Meter* this) {
- const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
- this->total = lpl->zram.totalZram;
- this->values[0] = lpl->zram.usedZramComp;
- this->values[1] = lpl->zram.usedZramOrig;
+ const LinuxMachine* lhost = (const LinuxMachine*) this->host;
+
+ this->total = lhost->zram.totalZram;
+ this->values[ZRAM_METER_COMPRESSED] = lhost->zram.usedZramComp;
+ this->values[ZRAM_METER_UNCOMPRESSED] = lhost->zram.usedZramOrig - lhost->zram.usedZramComp;
}
void Platform_setZfsArcValues(Meter* this) {
- const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
+ const LinuxMachine* lhost = (const LinuxMachine*) this->host;
- ZfsArcMeter_readStats(this, &(lpl->zfs));
+ ZfsArcMeter_readStats(this, &(lhost->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
- const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
+ const LinuxMachine* lhost = (const LinuxMachine*) this->host;
- ZfsCompressedArcMeter_readStats(this, &(lpl->zfs));
+ ZfsCompressedArcMeter_readStats(this, &(lhost->zfs));
}
char* Platform_getProcessEnv(pid_t pid) {
@@ -430,117 +468,90 @@ char* Platform_getProcessEnv(pid_t pid) {
return env;
}
-/*
- * Return the absolute path of a file given its pid&inode number
- *
- * Based on implementation of lslocks from util-linux:
- * https://sources.debian.org/src/util-linux/2.36-3/misc-utils/lslocks.c/#L162
- */
-char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
- struct stat sb;
- const struct dirent* de;
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData));
DIR* dirp;
- ssize_t len;
- int fd;
+ int dfd;
char path[PATH_MAX];
- char sym[PATH_MAX];
- char* ret = NULL;
-
- memset(path, 0, sizeof(path));
- memset(sym, 0, sizeof(sym));
-
- xSnprintf(path, sizeof(path), "%s/%d/fd/", PROCDIR, pid);
+ xSnprintf(path, sizeof(path), PROCDIR "/%d/fdinfo/", pid);
if (strlen(path) >= (sizeof(path) - 2))
- return NULL;
+ goto err;
if (!(dirp = opendir(path)))
- return NULL;
+ goto err;
- if ((fd = dirfd(dirp)) < 0 )
- goto out;
+ if ((dfd = dirfd(dirp)) == -1) {
+ closedir(dirp);
+ goto err;
+ }
- while ((de = readdir(dirp))) {
+ FileLocks_LockData** data_ref = &pdata->locks;
+ for (struct dirent* de; (de = readdir(dirp)); ) {
if (String_eq(de->d_name, ".") || String_eq(de->d_name, ".."))
continue;
- /* care only for numerical descriptors */
- if (!strtoull(de->d_name, (char **) NULL, 10))
+ errno = 0;
+ char* end = de->d_name;
+ int file = strtoull(de->d_name, &end, 10);
+ if (errno || *end)
continue;
- if (!Compat_fstatat(fd, path, de->d_name, &sb, 0) && inode != sb.st_ino)
+ int fd = openat(dfd, de->d_name, O_RDONLY | O_CLOEXEC);
+ if (fd == -1)
continue;
+ FILE* f = fdopen(fd, "r");
+ if (!f) {
+ close(fd);
+ continue;
+ }
- if ((len = Compat_readlinkat(fd, path, de->d_name, sym, sizeof(sym) - 1)) < 1)
- goto out;
-
- sym[len] = '\0';
-
- ret = xStrdup(sym);
- break;
- }
-
-out:
- closedir(dirp);
- return ret;
-}
+ for (char buffer[1024]; fgets(buffer, sizeof(buffer), f); ) {
+ if (!strchr(buffer, '\n'))
+ continue;
-FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
- FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData));
+ if (!String_startsWith(buffer, "lock:\t"))
+ continue;
- FILE* f = fopen(PROCDIR "/locks", "r");
- if (!f) {
- pdata->error = true;
- return pdata;
- }
+ FileLocks_Data data = {.fd = file};
+ int _;
+ unsigned int maj, min;
+ char lock_end[25], locktype[32], exclusive[32], readwrite[32];
+ if (10 != sscanf(buffer + strlen("lock:\t"), "%d: %31s %31s %31s %d %x:%x:%"PRIu64" %"PRIu64" %24s",
+ &_, locktype, exclusive, readwrite, &_,
+ &maj, &min, &data.inode,
+ &data.start, lock_end))
+ continue;
- char buffer[1024];
- FileLocks_LockData** data_ref = &pdata->locks;
- while(fgets(buffer, sizeof(buffer), f)) {
- if (!strchr(buffer, '\n'))
- continue;
+ data.locktype = xStrdup(locktype);
+ data.exclusive = xStrdup(exclusive);
+ data.readwrite = xStrdup(readwrite);
+ data.dev = makedev(maj, min);
- int lock_id;
- char lock_type[16];
- char lock_excl[16];
- char lock_rw[16];
- pid_t lock_pid;
- unsigned int lock_dev[2];
- uint64_t lock_inode;
- char lock_start[25];
- char lock_end[25];
-
- if (10 != sscanf(buffer, "%d: %15s %15s %15s %d %x:%x:%"PRIu64" %24s %24s",
- &lock_id, lock_type, lock_excl, lock_rw, &lock_pid,
- &lock_dev[0], &lock_dev[1], &lock_inode,
- lock_start, lock_end))
- continue;
+ if (String_eq(lock_end, "EOF"))
+ data.end = ULLONG_MAX;
+ else
+ data.end = strtoull(lock_end, NULL, 10);
- if (pid != lock_pid)
- continue;
+ xSnprintf(path, sizeof(path), PROCDIR "/%d/fd/%s", pid, de->d_name);
+ char link[PATH_MAX];
+ ssize_t link_len;
+ if (strlen(path) < (sizeof(path) - 2) && (link_len = readlink(path, link, sizeof(link))) != -1)
+ data.filename = xStrndup(link, link_len);
- FileLocks_LockData* ldata = xCalloc(1, sizeof(FileLocks_LockData));
- FileLocks_Data* data = &ldata->data;
- data->id = lock_id;
- data->locktype = xStrdup(lock_type);
- data->exclusive = xStrdup(lock_excl);
- data->readwrite = xStrdup(lock_rw);
- data->filename = Platform_getInodeFilename(lock_pid, lock_inode);
- data->dev[0] = lock_dev[0];
- data->dev[1] = lock_dev[1];
- data->inode = lock_inode;
- data->start = strtoull(lock_start, NULL, 10);
- if (!String_eq(lock_end, "EOF")) {
- data->end = strtoull(lock_end, NULL, 10);
- } else {
- data->end = ULLONG_MAX;
+ *data_ref = xCalloc(1, sizeof(FileLocks_LockData));
+ (*data_ref)->data = data;
+ data_ref = &(*data_ref)->next;
}
- *data_ref = ldata;
- data_ref = &ldata->next;
+ fclose(f);
}
- fclose(f);
+ closedir(dirp);
+ return pdata;
+
+err:
+ pdata->error = true;
return pdata;
}
@@ -562,6 +573,24 @@ void Platform_getPressureStall(const char* file, bool some, double* ten, double*
fclose(fd);
}
+void Platform_getFileDescriptors(double* used, double* max) {
+ *used = NAN;
+ *max = 65536;
+
+ FILE* fd = fopen(PROCDIR "/sys/fs/file-nr", "r");
+ if (!fd)
+ return;
+
+ unsigned long long v1, v2, v3;
+ int total = fscanf(fd, "%llu %llu %llu", &v1, &v2, &v3);
+ if (total == 3) {
+ *used = v1;
+ *max = v3;
+ }
+
+ fclose(fd);
+}
+
bool Platform_getDiskIO(DiskIOData* data) {
FILE* fd = fopen(PROCDIR "/diskstats", "r");
if (!fd)
@@ -815,7 +844,7 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
}
}
- if (!now && full && !isnan(capacityLevel))
+ if (!now && full && isNonnegative(capacityLevel))
totalRemain += capacityLevel * fullCharge;
} else if (type == AC) {
@@ -855,12 +884,12 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) {
if (Platform_Battery_method == BAT_PROC) {
Platform_Battery_getProcData(percent, isOnAC);
- if (isnan(*percent))
+ if (!isNonnegative(*percent))
Platform_Battery_method = BAT_SYS;
}
if (Platform_Battery_method == BAT_SYS) {
Platform_Battery_getSysData(percent, isOnAC);
- if (isnan(*percent))
+ if (!isNonnegative(*percent))
Platform_Battery_method = BAT_ERR;
}
if (Platform_Battery_method == BAT_ERR) {
@@ -899,7 +928,7 @@ CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
case 160: {
const char* mode = optarg;
if (!mode && optind < argc && argv[optind] != NULL &&
- (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
+ (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
mode = argv[optind++];
}
@@ -1040,7 +1069,7 @@ bool Platform_init(void) {
char lineBuffer[256];
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
// detect lxc or overlayfs and guess that this means we are running containerized
- if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay ")) {
+ if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay / overlay")) {
Running_containerized = true;
break;
}
diff --git a/linux/Platform.h b/linux/Platform.h
index e6fa161..e99d1a2 100644
--- a/linux/Platform.h
+++ b/linux/Platform.h
@@ -7,8 +7,6 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h"
-
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
@@ -23,15 +21,18 @@ in the source distribution for its full text.
#include "Macros.h"
#include "Meter.h"
#include "NetworkIOMeter.h"
+#include "Panel.h"
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "RichString.h"
+#include "Settings.h"
#include "SignalsPanel.h"
#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
+
/* GNU/Hurd does not have PATH_MAX in limits.h */
#ifndef PATH_MAX
#define PATH_MAX 4096
@@ -59,7 +60,7 @@ int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid(void);
+pid_t Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, unsigned int cpu);
@@ -75,12 +76,12 @@ void Platform_setZfsCompressedArcValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
-char* Platform_getInodeFilename(pid_t pid, ino_t inode);
-
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred);
+void Platform_getFileDescriptors(double* used, double* max);
+
bool Platform_getDiskIO(DiskIOData* data);
bool Platform_getNetworkIO(NetworkIOData* data);
@@ -114,7 +115,9 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) {
Generic_gettime_monotonic(msec);
}
-static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
+static inline Hashtable* Platform_dynamicMeters(void) {
+ return NULL;
+}
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
@@ -124,12 +127,30 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) {
static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }
-static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
+static inline Hashtable* Platform_dynamicColumns(void) {
+ return NULL;
+}
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
-static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
+static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
+ return NULL;
+}
+
+static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
+ return false;
+}
+
+static inline Hashtable* Platform_dynamicScreens(void) {
+ return NULL;
+}
+
+static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }
+
+static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }
+
+static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }
-static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
+static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }
#endif
diff --git a/linux/PressureStallMeter.c b/linux/PressureStallMeter.c
index e5089fc..f796247 100644
--- a/linux/PressureStallMeter.c
+++ b/linux/PressureStallMeter.c
@@ -6,6 +6,8 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "linux/PressureStallMeter.h"
#include <stdbool.h>
@@ -31,6 +33,8 @@ static void PressureStallMeter_updateValues(Meter* this) {
file = "cpu";
} else if (strstr(Meter_name(this), "IO")) {
file = "io";
+ } else if (strstr(Meter_name(this), "IRQ")) {
+ file = "irq";
} else {
file = "memory";
}
@@ -114,6 +118,23 @@ const MeterClass PressureStallIOFullMeter_class = {
.description = "Pressure Stall Information, full io"
};
+const MeterClass PressureStallIRQFullMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = PressureStallMeter_display,
+ },
+ .updateValues = PressureStallMeter_updateValues,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 3,
+ .total = 100.0,
+ .attributes = PressureStallMeter_attributes,
+ .name = "PressureStallIRQFull",
+ .uiName = "PSI full IRQ",
+ .caption = "PSI full IRQ: ",
+ .description = "Pressure Stall Information, full irq"
+};
+
const MeterClass PressureStallMemorySomeMeter_class = {
.super = {
.extends = Class(Meter),
diff --git a/linux/PressureStallMeter.h b/linux/PressureStallMeter.h
index 8acf46b..93ebd27 100644
--- a/linux/PressureStallMeter.h
+++ b/linux/PressureStallMeter.h
@@ -19,6 +19,8 @@ extern const MeterClass PressureStallIOSomeMeter_class;
extern const MeterClass PressureStallIOFullMeter_class;
+extern const MeterClass PressureStallIRQFullMeter_class;
+
extern const MeterClass PressureStallMemorySomeMeter_class;
extern const MeterClass PressureStallMemoryFullMeter_class;
diff --git a/linux/ProcessField.h b/linux/ProcessField.h
index 17cafa9..581a982 100644
--- a/linux/ProcessField.h
+++ b/linux/ProcessField.h
@@ -46,6 +46,8 @@ in the source distribution for its full text.
AUTOGROUP_ID = 127, \
AUTOGROUP_NICE = 128, \
CCGROUP = 129, \
+ CONTAINER = 130, \
+ M_PRIV = 131, \
// End of list
diff --git a/linux/SELinuxMeter.c b/linux/SELinuxMeter.c
index c35cb68..323c2a1 100644
--- a/linux/SELinuxMeter.c
+++ b/linux/SELinuxMeter.c
@@ -5,6 +5,8 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "linux/SELinuxMeter.h"
#include "CRT.h"
diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c
index 53ae2d2..e13c646 100644
--- a/linux/SystemdMeter.c
+++ b/linux/SystemdMeter.c
@@ -5,10 +5,13 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "linux/SystemdMeter.h"
#include <dlfcn.h>
#include <fcntl.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -39,6 +42,7 @@ in the source distribution for its full text.
typedef void sd_bus;
typedef void sd_bus_error;
static int (*sym_sd_bus_open_system)(sd_bus**);
+static int (*sym_sd_bus_open_user)(sd_bus**);
static int (*sym_sd_bus_get_property_string)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char**);
static int (*sym_sd_bus_get_property_trivial)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char, void*);
static sd_bus* (*sym_sd_bus_unref)(sd_bus*);
@@ -46,37 +50,43 @@ static void* dlopenHandle = NULL;
#endif /* BUILD_STATIC */
-#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
-static sd_bus* bus = NULL;
-#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
-
#define INVALID_VALUE ((unsigned int)-1)
-static char* systemState = NULL;
-static unsigned int nFailedUnits = INVALID_VALUE;
-static unsigned int nInstalledJobs = INVALID_VALUE;
-static unsigned int nNames = INVALID_VALUE;
-static unsigned int nJobs = INVALID_VALUE;
+typedef struct SystemdMeterContext {
+#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
+ sd_bus* bus;
+#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
+ char* systemState;
+ unsigned int nFailedUnits;
+ unsigned int nInstalledJobs;
+ unsigned int nNames;
+ unsigned int nJobs;
+} SystemdMeterContext_t;
+
+static SystemdMeterContext_t ctx_system;
+static SystemdMeterContext_t ctx_user;
static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
- free(systemState);
- systemState = NULL;
+ SystemdMeterContext_t* ctx = String_eq(Meter_name(this), "SystemdUser") ? &ctx_user : &ctx_system;
+
+ free(ctx->systemState);
+ ctx->systemState = NULL;
#ifdef BUILD_STATIC
# ifdef HAVE_LIBSYSTEMD
- if (bus) {
- sym_sd_bus_unref(bus);
+ if (ctx->bus) {
+ sym_sd_bus_unref(ctx->bus);
}
- bus = NULL;
+ ctx->bus = NULL;
# endif /* HAVE_LIBSYSTEMD */
#else /* BUILD_STATIC */
- if (bus && dlopenHandle) {
- sym_sd_bus_unref(bus);
+ if (ctx->bus && dlopenHandle) {
+ sym_sd_bus_unref(ctx->bus);
}
- bus = NULL;
+ ctx->bus = NULL;
- if (dlopenHandle) {
+ if (!ctx_system.systemState && !ctx_user.systemState && dlopenHandle) {
dlclose(dlopenHandle);
dlopenHandle = NULL;
}
@@ -84,7 +94,8 @@ static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
}
#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
-static int updateViaLib(void) {
+static int updateViaLib(bool user) {
+ SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;
#ifndef BUILD_STATIC
if (!dlopenHandle) {
dlopenHandle = dlopen("libsystemd.so.0", RTLD_LAZY);
@@ -101,6 +112,7 @@ static int updateViaLib(void) {
} while(0)
resolve(sd_bus_open_system);
+ resolve(sd_bus_open_user);
resolve(sd_bus_get_property_string);
resolve(sd_bus_get_property_trivial);
resolve(sd_bus_unref);
@@ -110,10 +122,13 @@ static int updateViaLib(void) {
#endif /* !BUILD_STATIC */
int r;
-
/* Connect to the system bus */
- if (!bus) {
- r = sym_sd_bus_open_system(&bus);
+ if (!ctx->bus) {
+ if (user) {
+ r = sym_sd_bus_open_user(&ctx->bus);
+ } else {
+ r = sym_sd_bus_open_system(&ctx->bus);
+ }
if (r < 0)
goto busfailure;
}
@@ -122,57 +137,57 @@ static int updateViaLib(void) {
static const char* const busObjectPath = "/org/freedesktop/systemd1";
static const char* const busInterfaceName = "org.freedesktop.systemd1.Manager";
- r = sym_sd_bus_get_property_string(bus,
+ r = sym_sd_bus_get_property_string(ctx->bus,
busServiceName, /* service to contact */
busObjectPath, /* object path */
busInterfaceName, /* interface name */
"SystemState", /* property name */
NULL, /* object to return error in */
- &systemState);
+ &ctx->systemState);
if (r < 0)
goto busfailure;
- r = sym_sd_bus_get_property_trivial(bus,
+ r = sym_sd_bus_get_property_trivial(ctx->bus,
busServiceName, /* service to contact */
busObjectPath, /* object path */
busInterfaceName, /* interface name */
"NFailedUnits", /* property name */
NULL, /* object to return error in */
'u', /* property type */
- &nFailedUnits);
+ &ctx->nFailedUnits);
if (r < 0)
goto busfailure;
- r = sym_sd_bus_get_property_trivial(bus,
+ r = sym_sd_bus_get_property_trivial(ctx->bus,
busServiceName, /* service to contact */
busObjectPath, /* object path */
busInterfaceName, /* interface name */
"NInstalledJobs", /* property name */
NULL, /* object to return error in */
'u', /* property type */
- &nInstalledJobs);
+ &ctx->nInstalledJobs);
if (r < 0)
goto busfailure;
- r = sym_sd_bus_get_property_trivial(bus,
+ r = sym_sd_bus_get_property_trivial(ctx->bus,
busServiceName, /* service to contact */
busObjectPath, /* object path */
busInterfaceName, /* interface name */
"NNames", /* property name */
NULL, /* object to return error in */
'u', /* property type */
- &nNames);
+ &ctx->nNames);
if (r < 0)
goto busfailure;
- r = sym_sd_bus_get_property_trivial(bus,
+ r = sym_sd_bus_get_property_trivial(ctx->bus,
busServiceName, /* service to contact */
busObjectPath, /* object path */
busInterfaceName, /* interface name */
"NJobs", /* property name */
NULL, /* object to return error in */
'u', /* property type */
- &nJobs);
+ &ctx->nJobs);
if (r < 0)
goto busfailure;
@@ -180,8 +195,8 @@ static int updateViaLib(void) {
return 0;
busfailure:
- sym_sd_bus_unref(bus);
- bus = NULL;
+ sym_sd_bus_unref(ctx->bus);
+ ctx->bus = NULL;
return -2;
#ifndef BUILD_STATIC
@@ -195,7 +210,9 @@ dlfailure:
}
#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
-static void updateViaExec(void) {
+static void updateViaExec(bool user) {
+ SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;
+
if (Settings_isReadonly())
return;
@@ -225,12 +242,13 @@ static void updateViaExec(void) {
"systemctl",
"systemctl",
"show",
+ user ? "--user" : "--system",
"--property=SystemState",
"--property=NFailedUnits",
"--property=NNames",
"--property=NJobs",
"--property=NInstalledJobs",
- (char *)NULL);
+ (char*)NULL);
exit(127);
}
close(fdpair[1]);
@@ -254,15 +272,15 @@ static void updateViaExec(void) {
if (newline) {
*newline = '\0';
}
- free_and_xStrdup(&systemState, lineBuffer + strlen("SystemState="));
+ free_and_xStrdup(&ctx->systemState, lineBuffer + strlen("SystemState="));
} else if (String_startsWith(lineBuffer, "NFailedUnits=")) {
- nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10);
+ ctx->nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10);
} else if (String_startsWith(lineBuffer, "NNames=")) {
- nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10);
+ ctx->nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10);
} else if (String_startsWith(lineBuffer, "NJobs=")) {
- nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10);
+ ctx->nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10);
} else if (String_startsWith(lineBuffer, "NInstalledJobs=")) {
- nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10);
+ ctx->nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10);
}
}
@@ -270,28 +288,31 @@ static void updateViaExec(void) {
}
static void SystemdMeter_updateValues(Meter* this) {
- free(systemState);
- systemState = NULL;
- nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE;
+ bool user = String_eq(Meter_name(this), "SystemdUser");
+ SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;
+
+ free(ctx->systemState);
+ ctx->systemState = NULL;
+ ctx->nFailedUnits = ctx->nInstalledJobs = ctx->nNames = ctx->nJobs = INVALID_VALUE;
#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
- if (updateViaLib() < 0)
- updateViaExec();
+ if (updateViaLib(user) < 0)
+ updateViaExec(user);
#else
- updateViaExec();
+ updateViaExec(user);
#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
- xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", systemState ? systemState : "???");
+ xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", ctx->systemState ? ctx->systemState : "???");
}
static int zeroDigitColor(unsigned int value) {
switch (value) {
- case 0:
- return CRT_colors[METER_VALUE];
- case INVALID_VALUE:
- return CRT_colors[METER_VALUE_ERROR];
- default:
- return CRT_colors[METER_VALUE_NOTICE];
+ case 0:
+ return CRT_colors[METER_VALUE];
+ case INVALID_VALUE:
+ return CRT_colors[METER_VALUE_ERROR];
+ default:
+ return CRT_colors[METER_VALUE_NOTICE];
}
}
@@ -307,60 +328,72 @@ static int valueDigitColor(unsigned int value) {
}
-static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
+static void _SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out, SystemdMeterContext_t* ctx) {
char buffer[16];
int len;
+ int color = METER_VALUE_ERROR;
- int color = (systemState && String_eq(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR;
- RichString_writeAscii(out, CRT_colors[color], systemState ? systemState : "N/A");
+ if (ctx->systemState) {
+ color = String_eq(ctx->systemState, "running") ? METER_VALUE_OK :
+ String_eq(ctx->systemState, "degraded") ? METER_VALUE_ERROR : METER_VALUE_WARN;
+ }
+ RichString_writeAscii(out, CRT_colors[color], ctx->systemState ? ctx->systemState : "N/A");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " (");
- if (nFailedUnits == INVALID_VALUE) {
+ if (ctx->nFailedUnits == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
len = 1;
} else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nFailedUnits);
}
- RichString_appendnAscii(out, zeroDigitColor(nFailedUnits), buffer, len);
+ RichString_appendnAscii(out, zeroDigitColor(ctx->nFailedUnits), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
- if (nNames == INVALID_VALUE) {
+ if (ctx->nNames == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
len = 1;
} else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", nNames);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nNames);
}
- RichString_appendnAscii(out, valueDigitColor(nNames), buffer, len);
+ RichString_appendnAscii(out, valueDigitColor(ctx->nNames), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) (");
- if (nJobs == INVALID_VALUE) {
+ if (ctx->nJobs == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
len = 1;
} else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", nJobs);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nJobs);
}
- RichString_appendnAscii(out, zeroDigitColor(nJobs), buffer, len);
+ RichString_appendnAscii(out, zeroDigitColor(ctx->nJobs), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
- if (nInstalledJobs == INVALID_VALUE) {
+ if (ctx->nInstalledJobs == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
len = 1;
} else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nInstalledJobs);
}
- RichString_appendnAscii(out, valueDigitColor(nInstalledJobs), buffer, len);
+ RichString_appendnAscii(out, valueDigitColor(ctx->nInstalledJobs), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)");
}
+static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
+ _SystemdMeter_display(cast, out, &ctx_system);
+}
+
+static void SystemdUserMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
+ _SystemdMeter_display(cast, out, &ctx_user);
+}
+
static const int SystemdMeter_attributes[] = {
METER_VALUE
};
@@ -382,3 +415,21 @@ const MeterClass SystemdMeter_class = {
.description = "Systemd system state and unit overview",
.caption = "Systemd: ",
};
+
+const MeterClass SystemdUserMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = SystemdUserMeter_display
+ },
+ .updateValues = SystemdMeter_updateValues,
+ .done = SystemdMeter_done,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 0,
+ .total = 100.0,
+ .attributes = SystemdMeter_attributes,
+ .name = "SystemdUser",
+ .uiName = "Systemd user state",
+ .description = "Systemd user state and unit overview",
+ .caption = "Systemd User: ",
+};
diff --git a/linux/SystemdMeter.h b/linux/SystemdMeter.h
index a05e087..50a793b 100644
--- a/linux/SystemdMeter.h
+++ b/linux/SystemdMeter.h
@@ -13,4 +13,6 @@ in the source distribution for its full text.
extern const MeterClass SystemdMeter_class;
+extern const MeterClass SystemdUserMeter_class;
+
#endif /* HEADER_SystemdMeter */
diff --git a/linux/ZramMeter.c b/linux/ZramMeter.c
index e1e27b7..8329f01 100644
--- a/linux/ZramMeter.c
+++ b/linux/ZramMeter.c
@@ -1,3 +1,13 @@
+/*
+htop - linux/ZramMeter.c
+(C) 2020 Murloc Knight
+(C) 2020-2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
#include "linux/ZramMeter.h"
#include <stddef.h>
@@ -7,10 +17,12 @@
#include "Object.h"
#include "Platform.h"
#include "RichString.h"
+#include "ZramMeter.h"
-static const int ZramMeter_attributes[] = {
- ZRAM
+static const int ZramMeter_attributes[ZRAM_METER_ITEMCOUNT] = {
+ [ZRAM_METER_COMPRESSED] = ZRAM_COMPRESSED,
+ [ZRAM_METER_UNCOMPRESSED] = ZRAM_UNCOMPRESSED,
};
static void ZramMeter_updateValues(Meter* this) {
@@ -20,15 +32,13 @@ static void ZramMeter_updateValues(Meter* this) {
Platform_setZramValues(this);
- /* on print bar for compressed data size, not uncompressed */
- this->curItems = 1;
-
- written = Meter_humanUnit(buffer, this->values[0], size);
+ written = Meter_humanUnit(buffer, this->values[ZRAM_METER_COMPRESSED], size);
METER_BUFFER_CHECK(buffer, size, written);
METER_BUFFER_APPEND_CHR(buffer, size, '(');
- written = Meter_humanUnit(buffer, this->values[1], size);
+ double uncompressed = this->values[ZRAM_METER_COMPRESSED] + this->values[ZRAM_METER_UNCOMPRESSED];
+ written = Meter_humanUnit(buffer, uncompressed, size);
METER_BUFFER_CHECK(buffer, size, written);
METER_BUFFER_APPEND_CHR(buffer, size, ')');
@@ -47,11 +57,12 @@ static void ZramMeter_display(const Object* cast, RichString* out) {
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
- Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
+ Meter_humanUnit(buffer, this->values[ZRAM_METER_COMPRESSED], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
- Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
+ double uncompressed = this->values[ZRAM_METER_COMPRESSED] + this->values[ZRAM_METER_UNCOMPRESSED];
+ Meter_humanUnit(buffer, uncompressed, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " uncompressed:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
}
@@ -64,7 +75,7 @@ const MeterClass ZramMeter_class = {
},
.updateValues = ZramMeter_updateValues,
.defaultMode = BAR_METERMODE,
- .maxItems = 2,
+ .maxItems = ZRAM_METER_ITEMCOUNT,
.total = 100.0,
.attributes = ZramMeter_attributes,
.name = "Zram",
diff --git a/linux/ZramMeter.h b/linux/ZramMeter.h
index ddba1ba..14a5215 100644
--- a/linux/ZramMeter.h
+++ b/linux/ZramMeter.h
@@ -1,8 +1,20 @@
#ifndef HEADER_ZramMeter
#define HEADER_ZramMeter
+/*
+htop - linux/ZramMeter.h
+(C) 2020 Murloc Knight
+(C) 2020-2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
#include "Meter.h"
+typedef enum {
+ ZRAM_METER_COMPRESSED = 0,
+ ZRAM_METER_UNCOMPRESSED = 1,
+ ZRAM_METER_ITEMCOUNT = 2, // number of entries in this enum
+} ZramMeterValues;
extern const MeterClass ZramMeter_class;
diff --git a/linux/ZramStats.h b/linux/ZramStats.h
index 67aadcc..f71a6c2 100644
--- a/linux/ZramStats.h
+++ b/linux/ZramStats.h
@@ -1,5 +1,13 @@
#ifndef HEADER_ZramStats
#define HEADER_ZramStats
+/*
+htop - ZramStats.h
+(C) 2020 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "ProcessTable.h"
typedef struct ZramStats_ {
memory_t totalZram;
diff --git a/linux/ZswapStats.h b/linux/ZswapStats.h
new file mode 100644
index 0000000..29e516f
--- /dev/null
+++ b/linux/ZswapStats.h
@@ -0,0 +1,19 @@
+#ifndef HEADER_ZswapStats
+#define HEADER_ZswapStats
+/*
+htop - ZswapStats.h
+(C) 2022 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "ProcessTable.h"
+
+typedef struct ZswapStats_ {
+ /* amount of RAM used by the zswap pool */
+ memory_t usedZswapComp;
+ /* amount of data stored inside the zswap pool */
+ memory_t usedZswapOrig;
+} ZswapStats;
+
+#endif

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