aboutsummaryrefslogtreecommitdiffstats
path: root/linux
diff options
context:
space:
mode:
authorDaniel Lange <DLange@git.local>2021-09-21 08:35:19 +0200
committerDaniel Lange <DLange@git.local>2021-09-21 08:35:19 +0200
commit69f439eff387a6ecb52734e400b297a3c85f2285 (patch)
tree2a988b7868b749654796183ba70b4272965da1bf /linux
parentc55320e9e2a8916e911bcd39ab37b79e3a7d03b2 (diff)
downloaddebian_htop-69f439eff387a6ecb52734e400b297a3c85f2285.tar.gz
debian_htop-69f439eff387a6ecb52734e400b297a3c85f2285.tar.bz2
debian_htop-69f439eff387a6ecb52734e400b297a3c85f2285.zip
New upstream version 3.1.0upstream/3.1.0
Diffstat (limited to 'linux')
-rw-r--r--linux/HugePageMeter.c106
-rw-r--r--linux/HugePageMeter.h15
-rw-r--r--linux/IOPriority.h2
-rw-r--r--linux/IOPriorityPanel.c3
-rw-r--r--linux/IOPriorityPanel.h2
-rw-r--r--linux/LibSensors.c218
-rw-r--r--linux/LibSensors.h11
-rw-r--r--linux/LinuxProcess.c756
-rw-r--r--linux/LinuxProcess.h82
-rw-r--r--linux/LinuxProcessList.c970
-rw-r--r--linux/LinuxProcessList.h17
-rw-r--r--linux/Platform.c387
-rw-r--r--linux/Platform.h75
-rw-r--r--linux/PressureStallMeter.c20
-rw-r--r--linux/PressureStallMeter.h1
-rw-r--r--linux/ProcessField.h6
-rw-r--r--linux/SELinuxMeter.c11
-rw-r--r--linux/SELinuxMeter.h1
-rw-r--r--linux/SystemdMeter.c98
-rw-r--r--linux/SystemdMeter.h1
-rw-r--r--linux/ZramMeter.c8
-rw-r--r--linux/ZramMeter.h1
-rw-r--r--linux/ZramStats.h6
23 files changed, 1545 insertions, 1252 deletions
diff --git a/linux/HugePageMeter.c b/linux/HugePageMeter.c
new file mode 100644
index 0000000..8c637fd
--- /dev/null
+++ b/linux/HugePageMeter.c
@@ -0,0 +1,106 @@
+/*
+htop - HugePageMeter.c
+(C) 2021 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "linux/HugePageMeter.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stddef.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Object.h"
+#include "ProcessList.h"
+#include "RichString.h"
+#include "linux/LinuxProcessList.h"
+
+
+static const char* HugePageMeter_active_labels[4] = { NULL, NULL, NULL, NULL };
+
+static const int HugePageMeter_attributes[] = {
+ HUGEPAGE_1,
+ HUGEPAGE_2,
+ HUGEPAGE_3,
+ HUGEPAGE_4,
+};
+
+static const char* const HugePageMeter_labels[] = {
+ " 64K:", " 128K:", " 256K:", " 512K:",
+ " 1M:", " 2M:", " 4M:", " 8M:", " 16M:", " 32M:", " 64M:", " 128M:", " 256M:", " 512M:",
+ " 1G:", " 2G:", " 4G:", " 8G:", " 16G:", " 32G:", " 64G:", " 128G:", " 256G:", " 512G:",
+};
+
+static void HugePageMeter_updateValues(Meter* this) {
+ assert(ARRAYSIZE(HugePageMeter_labels) == HTOP_HUGEPAGE_COUNT);
+
+ char* buffer = this->txtBuffer;
+ size_t size = sizeof(this->txtBuffer);
+ int written;
+ memory_t usedTotal = 0;
+ unsigned nextUsed = 0;
+
+ const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
+ this->total = lpl->totalHugePageMem;
+ this->values[0] = 0;
+ HugePageMeter_active_labels[0] = " used:";
+ for (unsigned i = 1; i < ARRAYSIZE(HugePageMeter_active_labels); i++) {
+ this->values[i] = NAN;
+ HugePageMeter_active_labels[i] = NULL;
+ }
+ for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) {
+ memory_t value = lpl->usedHugePageMem[i];
+ if (value != MEMORY_MAX) {
+ this->values[nextUsed] = value;
+ usedTotal += value;
+ HugePageMeter_active_labels[nextUsed] = HugePageMeter_labels[i];
+ if (++nextUsed == ARRAYSIZE(HugePageMeter_active_labels)) {
+ break;
+ }
+ }
+ }
+
+ written = Meter_humanUnit(buffer, usedTotal, size);
+ METER_BUFFER_CHECK(buffer, size, written);
+
+ METER_BUFFER_APPEND_CHR(buffer, size, '/');
+
+ Meter_humanUnit(buffer, this->total, size);
+}
+
+static void HugePageMeter_display(const Object* cast, RichString* out) {
+ char buffer[50];
+ const Meter* this = (const Meter*)cast;
+
+ RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
+ Meter_humanUnit(buffer, this->total, sizeof(buffer));
+ RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
+
+ for (unsigned i = 0; i < ARRAYSIZE(HugePageMeter_active_labels); i++) {
+ if (isnan(this->values[i])) {
+ break;
+ }
+ RichString_appendAscii(out, CRT_colors[METER_TEXT], HugePageMeter_active_labels[i]);
+ Meter_humanUnit(buffer, this->values[i], sizeof(buffer));
+ RichString_appendAscii(out, CRT_colors[HUGEPAGE_1 + i], buffer);
+ }
+}
+
+const MeterClass HugePageMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = HugePageMeter_display,
+ },
+ .updateValues = HugePageMeter_updateValues,
+ .defaultMode = BAR_METERMODE,
+ .maxItems = ARRAYSIZE(HugePageMeter_active_labels),
+ .total = 100.0,
+ .attributes = HugePageMeter_attributes,
+ .name = "HugePages",
+ .uiName = "HugePages",
+ .caption = "HP"
+};
diff --git a/linux/HugePageMeter.h b/linux/HugePageMeter.h
new file mode 100644
index 0000000..e3b867d
--- /dev/null
+++ b/linux/HugePageMeter.h
@@ -0,0 +1,15 @@
+#ifndef HEADER_HugePageMeter
+#define HEADER_HugePageMeter
+/*
+htop - HugePageMeter.h
+(C) 2021 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Meter.h"
+
+
+extern const MeterClass HugePageMeter_class;
+
+#endif /* HEADER_HugePageMeter */
diff --git a/linux/IOPriority.h b/linux/IOPriority.h
index 551a7d5..2b37cb0 100644
--- a/linux/IOPriority.h
+++ b/linux/IOPriority.h
@@ -30,8 +30,6 @@ typedef int IOPriority;
#define IOPriority_tuple(class_, data_) (((class_) << IOPRIO_CLASS_SHIFT) | (data_))
-#define IOPriority_error 0xffffffff
-
#define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7)
diff --git a/linux/IOPriorityPanel.c b/linux/IOPriorityPanel.c
index 25d2199..1bcbf4b 100644
--- a/linux/IOPriorityPanel.c
+++ b/linux/IOPriorityPanel.c
@@ -5,7 +5,7 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "IOPriorityPanel.h"
+#include "linux/IOPriorityPanel.h"
#include <stdbool.h>
#include <stddef.h>
@@ -14,6 +14,7 @@ in the source distribution for its full text.
#include "ListItem.h"
#include "Object.h"
#include "XUtils.h"
+#include "IOPriority.h"
Panel* IOPriorityPanel_new(IOPriority currPrio) {
diff --git a/linux/IOPriorityPanel.h b/linux/IOPriorityPanel.h
index 2ac4b31..3aa7610 100644
--- a/linux/IOPriorityPanel.h
+++ b/linux/IOPriorityPanel.h
@@ -8,7 +8,7 @@ in the source distribution for its full text.
*/
#include "Panel.h"
-#include "IOPriority.h"
+#include "linux/IOPriority.h"
Panel* IOPriorityPanel_new(IOPriority currPrio);
diff --git a/linux/LibSensors.c b/linux/LibSensors.c
index 158829a..9a27fe5 100644
--- a/linux/LibSensors.c
+++ b/linux/LibSensors.c
@@ -1,27 +1,53 @@
-#include "LibSensors.h"
+#include "linux/LibSensors.h"
+
+#include "config.h"
#ifdef HAVE_SENSORS_SENSORS_H
+#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
+#include <limits.h>
#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
#include <sensors/sensors.h>
+#include "Macros.h"
#include "XUtils.h"
+#include "linux/LinuxProcessList.h"
+
+
+#ifdef BUILD_STATIC
+#define sym_sensors_init sensors_init
+#define sym_sensors_cleanup sensors_cleanup
+#define sym_sensors_get_detected_chips sensors_get_detected_chips
+#define sym_sensors_get_features sensors_get_features
+#define sym_sensors_get_subfeature sensors_get_subfeature
+#define sym_sensors_get_value sensors_get_value
+
+#else
static int (*sym_sensors_init)(FILE*);
static void (*sym_sensors_cleanup)(void);
static const sensors_chip_name* (*sym_sensors_get_detected_chips)(const sensors_chip_name*, int*);
-static int (*sym_sensors_snprintf_chip_name)(char*, size_t, const sensors_chip_name*);
static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*);
static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type);
static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*);
-static char* (*sym_sensors_get_label)(const sensors_chip_name*, const sensors_feature*);
static void* dlopenHandle = NULL;
-int LibSensors_init(FILE* input) {
+#endif /* BUILD_STATIC */
+
+int LibSensors_init(void) {
+#ifdef BUILD_STATIC
+
+ return sym_sensors_init(NULL);
+
+#else
+
if (!dlopenHandle) {
/* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without
matching symlinks (unless people install the -dev packages) */
@@ -45,16 +71,15 @@ int LibSensors_init(FILE* input) {
resolve(sensors_init);
resolve(sensors_cleanup);
resolve(sensors_get_detected_chips);
- resolve(sensors_snprintf_chip_name);
resolve(sensors_get_features);
resolve(sensors_get_subfeature);
resolve(sensors_get_value);
- resolve(sensors_get_label);
#undef resolve
}
- return sym_sensors_init(input);
+ return sym_sensors_init(NULL);
+
dlfailure:
if (dlopenHandle) {
@@ -62,108 +87,183 @@ dlfailure:
dlopenHandle = NULL;
}
return -1;
+
+#endif /* BUILD_STATIC */
}
void LibSensors_cleanup(void) {
+#ifdef BUILD_STATIC
+
+ sym_sensors_cleanup();
+
+#else
+
if (dlopenHandle) {
sym_sensors_cleanup();
dlclose(dlopenHandle);
dlopenHandle = NULL;
}
+
+#endif /* BUILD_STATIC */
}
-void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) {
- for (unsigned int i = 0; i <= cpuCount; i++)
- cpus[i].temperature = NAN;
+int LibSensors_reload(void) {
+#ifndef BUILD_STATIC
+ if (!dlopenHandle) {
+ errno = ENOTSUP;
+ return -1;
+ }
+#endif /* !BUILD_STATIC */
+
+ sym_sensors_cleanup();
+ return sym_sensors_init(NULL);
+}
+
+static int tempDriverPriority(const sensors_chip_name* chip) {
+ static const struct TempDriverDefs {
+ const char* prefix;
+ int priority;
+ } tempDrivers[] = {
+ { "coretemp", 0 },
+ { "via_cputemp", 0 },
+ { "cpu_thermal", 0 },
+ { "k10temp", 0 },
+ { "zenpower", 0 },
+ /* Low priority drivers */
+ { "acpitz", 1 },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE(tempDrivers); i++)
+ if (String_eq(chip->prefix, tempDrivers[i].prefix))
+ return tempDrivers[i].priority;
+
+ return -1;
+}
+void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs) {
+ assert(existingCPUs > 0 && existingCPUs < 16384);
+ double data[existingCPUs + 1];
+ for (size_t i = 0; i < existingCPUs + 1; i++)
+ data[i] = NAN;
+
+#ifndef BUILD_STATIC
if (!dlopenHandle)
- return;
+ goto out;
+#endif /* !BUILD_STATIC */
unsigned int coreTempCount = 0;
+ int topPriority = 99;
int n = 0;
- for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
- char buffer[32];
- sym_sensors_snprintf_chip_name(buffer, sizeof(buffer), chip);
- if (!String_startsWith(buffer, "coretemp") &&
- !String_startsWith(buffer, "cpu_thermal") &&
- !String_startsWith(buffer, "k10temp") &&
- !String_startsWith(buffer, "zenpower"))
+ for (const sensors_chip_name* chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
+ const int priority = tempDriverPriority(chip);
+ if (priority < 0)
+ continue;
+
+ if (priority > topPriority)
continue;
+ if (priority < topPriority) {
+ /* Clear data from lower priority sensor */
+ for (size_t i = 0; i < existingCPUs + 1; i++)
+ data[i] = NAN;
+ }
+
+ topPriority = priority;
+
int m = 0;
- for (const sensors_feature *feature = sym_sensors_get_features(chip, &m); feature; feature = sym_sensors_get_features(chip, &m)) {
+ for (const sensors_feature* feature = sym_sensors_get_features(chip, &m); feature; feature = sym_sensors_get_features(chip, &m)) {
if (feature->type != SENSORS_FEATURE_TEMP)
continue;
- char* label = sym_sensors_get_label(chip, feature);
- if (!label)
+ if (!feature->name || !String_startsWith(feature->name, "temp"))
continue;
- unsigned int tempId;
- if (String_startsWith(label, "Package ")) {
- tempId = 0;
- } else if (String_startsWith(label, "temp")) {
- /* Raspberry Pi has only temp1 */
- tempId = 0;
- } else if (String_startsWith(label, "Tdie")) {
- tempId = 0;
- } else if (String_startsWith(label, "Core ")) {
- tempId = 1 + atoi(label + strlen("Core "));
- } else {
- tempId = UINT_MAX;
- }
+ unsigned long int tempID = strtoul(feature->name + strlen("temp"), NULL, 10);
+ if (tempID == 0 || tempID == ULONG_MAX)
+ continue;
- free(label);
+ /* Feature name IDs start at 1, adjust to start at 0 to match data indices */
+ tempID--;
- if (tempId > cpuCount)
+ if (tempID > existingCPUs)
continue;
- const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
- if (sub_feature) {
- double temp;
- int r = sym_sensors_get_value(chip, sub_feature->number, &temp);
- if (r != 0)
- continue;
+ const sensors_subfeature* subFeature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
+ if (!subFeature)
+ continue;
- cpus[tempId].temperature = temp;
- if (tempId > 0)
+ double temp;
+ int r = sym_sensors_get_value(chip, subFeature->number, &temp);
+ if (r != 0)
+ continue;
+
+ /* If already set, e.g. Ryzen reporting platform temperature for each die, use the bigger one */
+ if (isnan(data[tempID])) {
+ data[tempID] = temp;
+ if (tempID > 0)
coreTempCount++;
+ } else {
+ data[tempID] = MAXIMUM(data[tempID], temp);
}
}
}
- const double packageTemp = cpus[0].temperature;
+ /* Adjust data for chips not providing a platform temperature */
+ if (coreTempCount + 1 == activeCPUs || coreTempCount + 1 == activeCPUs / 2) {
+ memmove(&data[1], &data[0], existingCPUs * sizeof(*data));
+ data[0] = NAN;
+ coreTempCount++;
- /* Only package temperature - copy to all cpus */
- if (coreTempCount == 0 && !isnan(packageTemp)) {
- for (unsigned int i = 1; i <= cpuCount; i++)
- cpus[i].temperature = packageTemp;
+ /* Check for further adjustments */
+ }
- return;
+ /* Only package temperature - copy to all cores */
+ if (coreTempCount == 0 && !isnan(data[0])) {
+ for (unsigned int i = 1; i <= existingCPUs; i++)
+ data[i] = data[0];
+
+ /* No further adjustments */
+ goto out;
}
/* No package temperature - set to max core temperature */
- if (isnan(packageTemp) && coreTempCount != 0) {
+ if (isnan(data[0]) && coreTempCount != 0) {
double maxTemp = NAN;
- for (unsigned int i = 1; i <= cpuCount; i++) {
- const double coreTemp = cpus[i].temperature;
- if (isnan(coreTemp))
+ for (unsigned int i = 1; i <= existingCPUs; i++) {
+ if (isnan(data[i]))
continue;
- maxTemp = MAXIMUM(maxTemp, coreTemp);
+ maxTemp = MAXIMUM(maxTemp, data[i]);
}
- cpus[0].temperature = maxTemp;
+ 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])) {
+ for (unsigned int i = 2; i <= existingCPUs; i++)
+ data[i] = data[1];
+
+ /* No further adjustments */
+ goto out;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
- const unsigned int delta = cpuCount / 2;
+ const unsigned int delta = activeCPUs / 2;
if (coreTempCount == delta) {
- for (unsigned int i = 1; i <= delta; i++)
- cpus[i + delta].temperature = cpus[i].temperature;
+ memcpy(&data[delta + 1], &data[1], delta * sizeof(*data));
+
+ /* No further adjustments */
+ goto out;
}
+
+out:
+ for (unsigned int i = 0; i <= existingCPUs; i++)
+ cpus[i].temperature = data[i];
}
#endif /* HAVE_SENSORS_SENSORS_H */
diff --git a/linux/LibSensors.h b/linux/LibSensors.h
index cceeedb..aa89979 100644
--- a/linux/LibSensors.h
+++ b/linux/LibSensors.h
@@ -1,16 +1,13 @@
#ifndef HEADER_LibSensors
#define HEADER_LibSensors
-#include "config.h" // IWYU pragma: keep
+#include "linux/LinuxProcessList.h"
-#include <stdio.h>
-#include "LinuxProcessList.h"
-
-
-int LibSensors_init(FILE* input);
+int LibSensors_init(void);
void LibSensors_cleanup(void);
+int LibSensors_reload(void);
-void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount);
+void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs);
#endif /* HEADER_LibSensors */
diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c
index ce6d34d..75638e4 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -6,12 +6,11 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "LinuxProcess.h"
+#include "linux/LinuxProcess.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <syscall.h>
#include <unistd.h>
@@ -21,15 +20,13 @@ in the source distribution for its full text.
#include "ProvideCurses.h"
#include "RichString.h"
#include "XUtils.h"
+#include "linux/IOPriority.h"
/* semi-global */
int pageSize;
int pageSizeKB;
-/* Used to identify kernel threads in Comm and Exe columns */
-static const char *const kthreadID = "KTHREAD";
-
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, },
@@ -38,34 +35,34 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
- [TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
+ [TTY] = { .name = "TTY", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
- [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
- [CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, },
- [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
- [CMAJFLT] = { .name = "CMAJFLT", .title = " CMAJFLT ", .description = "Children processes' major faults", .flags = 0, },
- [UTIME] = { .name = "UTIME", .title = " UTIME+ ", .description = "User CPU time - time the process spent executing in user mode", .flags = 0, },
- [STIME] = { .name = "STIME", .title = " STIME+ ", .description = "System CPU time - time the kernel spent running system calls for this process", .flags = 0, },
- [CUTIME] = { .name = "CUTIME", .title = " CUTIME+ ", .description = "Children processes' user CPU time", .flags = 0, },
- [CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, },
+ [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, .defaultSortDesc = true, },
+ [CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, .defaultSortDesc = true, },
+ [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, .defaultSortDesc = true, },
+ [CMAJFLT] = { .name = "CMAJFLT", .title = " CMAJFLT ", .description = "Children processes' major faults", .flags = 0, .defaultSortDesc = true, },
+ [UTIME] = { .name = "UTIME", .title = " UTIME+ ", .description = "User CPU time - time the process spent executing in user mode", .flags = 0, .defaultSortDesc = true, },
+ [STIME] = { .name = "STIME", .title = " STIME+ ", .description = "System CPU time - time the kernel spent running system calls for this process", .flags = 0, .defaultSortDesc = true, },
+ [CUTIME] = { .name = "CUTIME", .title = " CUTIME+ ", .description = "Children processes' user CPU time", .flags = 0, .defaultSortDesc = true, },
+ [CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, .defaultSortDesc = true, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
+ [ELAPSED] = { .name = "ELAPSED", .title = "ELAPSED ", .description = "Time since the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
- [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
- [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
- [M_SHARE] = { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, },
- [M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, },
- [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, },
- [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, },
- [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, },
+ [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_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, },
- [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, },
- [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .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, },
+ [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, },
+ [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
- [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
- [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
+ [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
+ [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
#ifdef HAVE_OPENVZ
[CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
@@ -74,45 +71,36 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
#ifdef HAVE_VSERVER
[VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, },
#endif
- [RCHAR] = { .name = "RCHAR", .title = " RD_CHAR ", .description = "Number of bytes the process has read", .flags = PROCESS_FLAG_IO, },
- [WCHAR] = { .name = "WCHAR", .title = " WR_CHAR ", .description = "Number of bytes the process has written", .flags = PROCESS_FLAG_IO, },
- [SYSCR] = { .name = "SYSCR", .title = " RD_SYSC ", .description = "Number of read(2) syscalls for the process", .flags = PROCESS_FLAG_IO, },
- [SYSCW] = { .name = "SYSCW", .title = " WR_SYSC ", .description = "Number of write(2) syscalls for the process", .flags = PROCESS_FLAG_IO, },
- [RBYTES] = { .name = "RBYTES", .title = " IO_RBYTES ", .description = "Bytes of read(2) I/O for the process", .flags = PROCESS_FLAG_IO, },
- [WBYTES] = { .name = "WBYTES", .title = " IO_WBYTES ", .description = "Bytes of write(2) I/O for the process", .flags = PROCESS_FLAG_IO, },
- [CNCLWB] = { .name = "CNCLWB", .title = " IO_CANCEL ", .description = "Bytes of cancelled write(2) I/O", .flags = PROCESS_FLAG_IO, },
- [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, },
- [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, },
- [IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, },
+ [RCHAR] = { .name = "RCHAR", .title = "RCHAR ", .description = "Number of bytes the process has read", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
+ [WCHAR] = { .name = "WCHAR", .title = "WCHAR ", .description = "Number of bytes the process has written", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
+ [SYSCR] = { .name = "SYSCR", .title = " READ_SYSC ", .description = "Number of read(2) syscalls for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
+ [SYSCW] = { .name = "SYSCW", .title = " WRITE_SYSC ", .description = "Number of write(2) syscalls for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
+ [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_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 ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, },
- [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
+ [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
- [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
- [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
- [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
+ [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD%", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
+ [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
+ [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWPD%", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
#endif
- [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
- [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, },
- [M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
- [CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, },
+ [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
+ [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
+ [M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
+ [CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, .defaultSortDesc = true, },
[SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
- [CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, },
+ [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, },
};
-/* This function returns the string displayed in Command column, so that sorting
- * happens on what is displayed - whether comm, full path, basename, etc.. So
- * this follows LinuxProcess_writeField(COMM) and LinuxProcess_writeCommand */
-static const char* LinuxProcess_getCommandStr(const Process *this) {
- const LinuxProcess *lp = (const LinuxProcess *)this;
- if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) {
- return this->comm;
- }
- return lp->mergedCommand.str;
-}
-
Process* LinuxProcess_new(const Settings* settings) {
LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess));
Object_setClass(this, Class(LinuxProcess));
@@ -127,12 +115,7 @@ void Process_delete(Object* cast) {
#ifdef HAVE_OPENVZ
free(this->ctid);
#endif
- free(this->cwd);
free(this->secattr);
- free(this->ttyDevice);
- free(this->procExe);
- free(this->procComm);
- free(this->mergedCommand.str);
free(this);
}
@@ -175,429 +158,35 @@ bool LinuxProcess_setIOPriority(Process* this, Arg ioprio) {
return (LinuxProcess_updateIOPriority((LinuxProcess*)this) == ioprio.i);
}
-#ifdef HAVE_DELAYACCT
-static void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
- if (isnan(delay_percent)) {
- xSnprintf(buffer, n, " N/A ");
- } else {
- xSnprintf(buffer, n, "%4.1f ", delay_percent);
- }
-}
-#endif
-
-/*
-TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is
-not available in an userspace header - so define it. Note: when colorizing a
-basename with the comm prefix, the entire basename (not just the comm prefix) is
-colorized for better readability, and it is implicit that only upto
-(TASK_COMM_LEN - 1) could be comm
-*/
-#define TASK_COMM_LEN 16
-
-static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdlineBasenameOffset, int *pCommStart, int *pCommEnd) {
- /* Try to find procComm in tokenized cmdline - this might in rare cases
- * mis-identify a string or fail, if comm or cmdline had been unsuitably
- * modified by the process */
- const char *tokenBase;
- size_t tokenLen;
- const size_t commLen = strlen(comm);
-
- for (const char *token = cmdline + cmdlineBasenameOffset; *token; ) {
- for (tokenBase = token; *token && *token != '\n'; ++token) {
- if (*token == '/') {
- tokenBase = token + 1;
- }
- }
- tokenLen = token - tokenBase;
-
- if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) &&
- strncmp(tokenBase, comm, commLen) == 0) {
- *pCommStart = tokenBase - cmdline;
- *pCommEnd = token - cmdline;
- return true;
- }
- if (*token) {
- do {
- ++token;
- } while ('\n' == *token);
- }
- }
- return false;
-}
-
-static int matchCmdlinePrefixWithExeSuffix(const char *cmdline, int cmdlineBaseOffset, const char *exe, int exeBaseOffset, int exeBaseLen) {
- int matchLen; /* matching length to be returned */
- char delim; /* delimiter following basename */
-
- /* cmdline prefix is an absolute path: it must match whole exe. */
- if (cmdline[0] == '/') {
- matchLen = exeBaseLen + exeBaseOffset;
- if (strncmp(cmdline, exe, matchLen) == 0) {
- delim = cmdline[matchLen];
- if (delim == 0 || delim == '\n' || delim == ' ') {
- return matchLen;
- }
- }
- return 0;
- }
-
- /* cmdline prefix is a relative path: We need to first match the basename at
- * cmdlineBaseOffset and then reverse match the cmdline prefix with the exe
- * suffix. But there is a catch: Some processes modify their cmdline in ways
- * that make htop's identification of the basename in cmdline unreliable.
- * For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to
- * "gdm-session-worker [pam/gdm-autologin]" and htop ends up with
- * procCmdlineBasenameOffset at "gdm-autologin]". This issue could arise with
- * chrome as well as it stores in cmdline its concatenated argument vector,
- * without NUL delimiter between the arguments (which may contain a '/')
- *
- * So if needed, we adjust cmdlineBaseOffset to the previous (if any)
- * component of the cmdline relative path, and retry the procedure. */
- bool delimFound; /* if valid basename delimiter found */
- do {
- /* match basename */
- matchLen = exeBaseLen + cmdlineBaseOffset;
- if (cmdlineBaseOffset < exeBaseOffset &&
- strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) {
- delim = cmdline[matchLen];
- if (delim == 0 || delim == '\n' || delim == ' ') {
- int i, j;
- /* reverse match the cmdline prefix and exe suffix */
- for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1;
- i >= 0 && cmdline[i] == exe[j]; --i, --j)
- ;
- /* full match, with exe suffix being a valid relative path */
- if (i < 0 && exe[j] == '/') {
- return matchLen;
- }
- }
- }
- /* Try to find the previous potential cmdlineBaseOffset - it would be
- * preceded by '/' or nothing, and delimited by ' ' or '\n' */
- for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) {
- if (delimFound) {
- if (cmdline[cmdlineBaseOffset - 1] == '/') {
- break;
- }
- } else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') {
- delimFound = true;
- }
- }
- } while (delimFound);
-
- return 0;
-}
-
-/* stpcpy, but also converts newlines to spaces */
-static inline char *stpcpyWithNewlineConversion(char *dstStr, const char *srcStr) {
- for (; *srcStr; ++srcStr) {
- *dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr;
- }
- *dstStr = 0;
- return dstStr;
+bool LinuxProcess_isAutogroupEnabled(void) {
+ char buf[16];
+ if (xReadfile(PROCDIR "/sys/kernel/sched_autogroup_enabled", buf, sizeof(buf)) < 0)
+ return false;
+ return buf[0] == '1';
}
-/*
-This function makes the merged Command string. It also stores the offsets of the
-basename, comm w.r.t the merged Command string - these offsets will be used by
-LinuxProcess_writeCommand() for coloring. The merged Command string is also
-returned by LinuxProcess_getCommandStr() for searching, sorting and filtering.
-*/
-void LinuxProcess_makeCommandStr(Process* this) {
- LinuxProcess *lp = (LinuxProcess *)this;
- LinuxProcessMergedCommand *mc = &lp->mergedCommand;
-
- bool showMergedCommand = this->settings->showMergedCommand;
- bool showProgramPath = this->settings->showProgramPath;
- bool searchCommInCmdline = this->settings->findCommInCmdline;
- bool stripExeFromCmdline = this->settings->stripExeFromCmdline;
-
- /* lp->mergedCommand.str needs updating only if its state or contents changed.
- * Its content is based on the fields cmdline, comm, and exe. */
- if (
- mc->prevMergeSet == showMergedCommand &&
- mc->prevPathSet == showProgramPath &&
- mc->prevCommSet == searchCommInCmdline &&
- mc->prevCmdlineSet == stripExeFromCmdline &&
- !mc->cmdlineChanged &&
- !mc->commChanged &&
- !mc->exeChanged
- ) {
- return;
- }
-
- /* The field separtor "│" has been chosen such that it will not match any
- * valid string used for searching or filtering */
- const char *SEPARATOR = CRT_treeStr[TREE_STR_VERT];
- const int SEPARATOR_LEN = strlen(SEPARATOR);
-
- /* Check for any changed fields since we last built this string */
- if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) {
- free(mc->str);
- /* Accommodate the column text, two field separators and terminating NUL */
- mc->str = xCalloc(1, mc->maxLen + 2*SEPARATOR_LEN + 1);
- }
-
- /* Preserve the settings used in this run */
- mc->prevMergeSet = showMergedCommand;
- mc->prevPathSet = showProgramPath;
- mc->prevCommSet = searchCommInCmdline;
- mc->prevCmdlineSet = stripExeFromCmdline;
-
- /* Mark everything as unchanged */
- mc->cmdlineChanged = false;
- mc->commChanged = false;
- mc->exeChanged = false;
-
- /* Clear any separators */
- mc->sep1 = 0;
- mc->sep2 = 0;
-
- /* Clear any highlighting locations */
- mc->baseStart = 0;
- mc->baseEnd = 0;
- mc->commStart = 0;
- mc->commEnd = 0;
-
- const char *cmdline = this->comm;
- const char *procExe = lp->procExe;
- const char *procComm = lp->procComm;
-
- char *strStart = mc->str;
- char *str = strStart;
-
- int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset;
- int cmdlineBasenameEnd = lp->procCmdlineBasenameEnd;
-
- if (!cmdline) {
- cmdlineBasenameOffset = 0;
- cmdlineBasenameEnd = 0;
- cmdline = "(zombie)";
- }
-
- assert(cmdlineBasenameOffset >= 0);
- assert(cmdlineBasenameOffset <= (int)strlen(cmdline));
-
- if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
- if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
- if (strncmp(cmdline + cmdlineBasenameOffset, procComm, MINIMUM(TASK_COMM_LEN - 1, strlen(procComm))) != 0) {
- mc->commStart = 0;
- mc->commEnd = strlen(procComm);
-
- str = stpcpy(str, procComm);
-
- mc->sep1 = str - strStart;
- str = stpcpy(str, SEPARATOR);
- }
- }
-
- if (showProgramPath) {
- (void) stpcpyWithNewlineConversion(str, cmdline);
- mc->baseStart = cmdlineBasenameOffset;
- mc->baseEnd = cmdlineBasenameEnd;
- } else {
- (void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset);
- mc->baseStart = 0;
- mc->baseEnd = cmdlineBasenameEnd - cmdlineBasenameOffset;
- }
-
- if (mc->sep1) {
- mc->baseStart += str - strStart - SEPARATOR_LEN + 1;
- mc->baseEnd += str - strStart - SEPARATOR_LEN + 1;
- }
-
- return;
- }
-
- int exeLen = lp->procExeLen;
- int exeBasenameOffset = lp->procExeBasenameOffset;
- int exeBasenameLen = exeLen - exeBasenameOffset;
-
- assert(exeBasenameOffset >= 0);
- assert(exeBasenameOffset <= (int)strlen(procExe));
-
- /* Start with copying exe */
- if (showProgramPath) {
- str = stpcpy(str, procExe);
- mc->baseStart = exeBasenameOffset;
- mc->baseEnd = exeLen;
- } else {
- str = stpcpy(str, procExe + exeBasenameOffset);
- mc->baseStart = 0;
- mc->baseEnd = exeBasenameLen;
- }
-
- mc->sep1 = 0;
- mc->sep2 = 0;
-
- int commStart = 0;
- int commEnd = 0;
- bool commInCmdline = false;
-
- /* Try to match procComm with procExe's basename: This is reliable (predictable) */
- if (strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0) {
- commStart = mc->baseStart;
- commEnd = mc->baseEnd;
- } else if (searchCommInCmdline) {
- /* commStart/commEnd will be adjusted later along with cmdline */
- commInCmdline = findCommInCmdline(procComm, cmdline, cmdlineBasenameOffset, &commStart, &commEnd);
- }
-
- int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameOffset, procExe, exeBasenameOffset, exeBasenameLen);
-
- /* Note: commStart, commEnd are offsets into RichString. But the multibyte
- * separator (with size SEPARATOR_LEN) has size 1 in RichString. The offset
- * adjustments below reflect this. */
- if (commEnd) {
- mc->unmatchedExe = !matchLen;
-
- if (matchLen) {
- /* strip the matched exe prefix */
- cmdline += matchLen;
-
- if (commInCmdline) {
- commStart += str - strStart - matchLen;
- commEnd += str - strStart - matchLen;
- }
- } else {
- /* cmdline will be a separate field */
- mc->sep1 = str - strStart;
- str = stpcpy(str, SEPARATOR);
-
- if (commInCmdline) {
- commStart += str - strStart - SEPARATOR_LEN + 1;
- commEnd += str - strStart - SEPARATOR_LEN + 1;
- }
- }
-
- mc->separateComm = false; /* procComm merged */
+bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) {
+ char buffer[256];
+ xSnprintf(buffer, sizeof(buffer), PROCDIR "/%d/autogroup", this->pid);
+
+ FILE* file = fopen(buffer, "r+");
+ if (!file)
+ return false;
+
+ long int identity;
+ int nice;
+ int ok = fscanf(file, "/autogroup-%ld nice %d", &identity, &nice);
+ bool success;
+ if (ok == 2) {
+ rewind(file);
+ xSnprintf(buffer, sizeof(buffer), "%d", nice + delta.i);
+ success = fputs(buffer, file) > 0;
} else {
- mc->sep1 = str - strStart;
- str = stpcpy(str, SEPARATOR);
-
- commStart = str - strStart - SEPARATOR_LEN + 1;
- str = stpcpy(str, procComm);
- commEnd = str - strStart - SEPARATOR_LEN + 1; /* or commStart + strlen(procComm) */
-
- mc->unmatchedExe = !matchLen;
-
- if (matchLen) {
- if (stripExeFromCmdline) {
- cmdline += matchLen;
- }
- }
-
- if (*cmdline) {
- mc->sep2 = str - strStart - SEPARATOR_LEN + 1;
- str = stpcpy(str, SEPARATOR);
- }
-
- mc->separateComm = true; /* procComm a separate field */
- }
-
- /* Display cmdline if it hasn't been consumed by procExe */
- if (*cmdline) {
- (void) stpcpyWithNewlineConversion(str, cmdline);
- }
-
- mc->commStart = commStart;
- mc->commEnd = commEnd;
-}
-
-static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAttr, RichString* str) {
- const LinuxProcess *lp = (const LinuxProcess *)this;
- const LinuxProcessMergedCommand *mc = &lp->mergedCommand;
-
- int strStart = RichString_size(str);
-
- int baseStart = strStart + lp->mergedCommand.baseStart;
- int baseEnd = strStart + lp->mergedCommand.baseEnd;
- int commStart = strStart + lp->mergedCommand.commStart;
- int commEnd = strStart + lp->mergedCommand.commEnd;
-
- int commAttr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
-
- bool highlightBaseName = this->settings->highlightBaseName;
-
- if(lp->procExeDeleted)
- baseAttr = CRT_colors[FAILED_READ];
-
- RichString_appendWide(str, attr, lp->mergedCommand.str);
-
- if (lp->mergedCommand.commEnd) {
- if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) {
- /* If it was matched with procExe's basename, make it bold if needed */
- if (commEnd > baseEnd) {
- RichString_setAttrn(str, A_BOLD | baseAttr, baseStart, baseEnd - baseStart);
- RichString_setAttrn(str, A_BOLD | commAttr, baseEnd, commEnd - baseEnd);
- } else if (commEnd < baseEnd) {
- RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - commStart);
- RichString_setAttrn(str, A_BOLD | baseAttr, commEnd, baseEnd - commEnd);
- } else {
- // Actually should be highlighted commAttr, but marked baseAttr to reduce visual noise
- RichString_setAttrn(str, A_BOLD | baseAttr, commStart, commEnd - commStart);
- }
-
- baseStart = baseEnd;
- } else {
- RichString_setAttrn(str, commAttr, commStart, commEnd - commStart);
- }
+ success = false;
}
- if (baseStart < baseEnd && highlightBaseName) {
- RichString_setAttrn(str, baseAttr, baseStart, baseEnd - baseStart);
- }
-
- if (mc->sep1)
- RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep1, 1);
- if (mc->sep2)
- RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep2, 1);
-}
-
-static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) {
- /* This code is from Process_writeField for COMM, but we invoke
- * LinuxProcess_writeCommand to display
- * /proc/pid/exe (or its basename)│/proc/pid/comm│/proc/pid/cmdline */
- int baseattr = CRT_colors[PROCESS_BASENAME];
- if (this->settings->highlightThreads && Process_isThread(this)) {
- attr = CRT_colors[PROCESS_THREAD];
- baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
- }
- if (!this->settings->treeView || this->indent == 0) {
- LinuxProcess_writeCommand(this, attr, baseattr, str);
- } else {
- char* buf = buffer;
- int maxIndent = 0;
- bool lastItem = (this->indent < 0);
- int indent = (this->indent < 0 ? -this->indent : this->indent);
- int vertLen = strlen(CRT_treeStr[TREE_STR_VERT]);
-
- for (int i = 0; i < 32; i++) {
- if (indent & (1U << i)) {
- maxIndent = i+1;
- }
- }
- for (int i = 0; i < maxIndent - 1; i++) {
- if (indent & (1 << i)) {
- if (buf - buffer + (vertLen + 3) > n) {
- break;
- }
- buf = stpcpy(buf, CRT_treeStr[TREE_STR_VERT]);
- buf = stpcpy(buf, " ");
- } else {
- if (buf - buffer + 4 > n) {
- break;
- }
- buf = stpcpy(buf, " ");
- }
- }
-
- n -= (buf - buffer);
- const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
- xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
- RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
- LinuxProcess_writeCommand(this, attr, baseattr, str);
- }
+ fclose(file);
+ return success;
}
static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) {
@@ -607,48 +196,38 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
int attr = CRT_colors[DEFAULT_COLOR];
size_t n = sizeof(buffer) - 1;
switch (field) {
- case TTY_NR: {
- if (lp->ttyDevice) {
- xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
- } else {
- attr = CRT_colors[PROCESS_SHADOW];
- xSnprintf(buffer, n, "? ");
- }
- break;
- }
- case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
- case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
- case M_DRS: Process_humanNumber(str, lp->m_drs * pageSizeKB, coloring); return;
- case M_DT: Process_humanNumber(str, lp->m_dt * pageSizeKB, coloring); return;
+ 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 M_LRS:
if (lp->m_lrs) {
- Process_humanNumber(str, lp->m_lrs * pageSizeKB, coloring);
+ Process_printBytes(str, lp->m_lrs * pageSize, coloring);
return;
}
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
break;
- case M_TRS: Process_humanNumber(str, lp->m_trs * pageSizeKB, coloring); return;
- case M_SHARE: Process_humanNumber(str, lp->m_share * pageSizeKB, coloring); return;
- case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return;
- case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return;
- case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return;
- case UTIME: Process_printTime(str, lp->utime); return;
- case STIME: Process_printTime(str, lp->stime); return;
- case CUTIME: Process_printTime(str, lp->cutime); return;
- case CSTIME: Process_printTime(str, lp->cstime); return;
- case RCHAR: Process_colorNumber(str, lp->io_rchar, coloring); return;
- case WCHAR: Process_colorNumber(str, lp->io_wchar, coloring); return;
- case SYSCR: Process_colorNumber(str, lp->io_syscr, coloring); return;
- case SYSCW: Process_colorNumber(str, lp->io_syscw, coloring); return;
- case RBYTES: Process_colorNumber(str, lp->io_read_bytes, coloring); return;
- case WBYTES: Process_colorNumber(str, lp->io_write_bytes, coloring); return;
- case CNCLWB: Process_colorNumber(str, lp->io_cancelled_write_bytes, coloring); return;
- case IO_READ_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps, coloring); return;
- case IO_WRITE_RATE: Process_outputRate(str, buffer, n, lp->io_rate_write_bps, coloring); return;
+ 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 = NAN;
+ 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))
@@ -657,7 +236,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
totalRate = lp->io_rate_write_bps;
else
totalRate = NAN;
- Process_outputRate(str, buffer, n, totalRate, coloring); return;
+ Process_printRate(str, totalRate, coloring);
+ return;
}
#ifdef HAVE_OPENVZ
case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break;
@@ -687,9 +267,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
break;
}
#ifdef HAVE_DELAYACCT
- case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break;
- case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break;
- case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break;
+ case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break;
+ case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break;
+ case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break;
#endif
case CTXT:
if (lp->ctxt_diff > 1000) {
@@ -698,60 +278,37 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
break;
case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break;
- case COMM: {
- if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) {
- Process_writeField(this, str, field);
- } else {
- LinuxProcess_writeCommandField(this, str, buffer, n, attr);
- }
- return;
- }
- case PROC_COMM: {
- const char* procComm;
- if (lp->procComm) {
- attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
- procComm = lp->procComm;
+ case AUTOGROUP_ID:
+ if (lp->autogroup_id != -1) {
+ xSnprintf(buffer, n, "%4ld ", lp->autogroup_id);
} else {
attr = CRT_colors[PROCESS_SHADOW];
- procComm = Process_isKernelThread(lp) ? kthreadID : "N/A";
+ xSnprintf(buffer, n, " N/A ");
}
- /* 15 being (TASK_COMM_LEN - 1) */
- Process_printLeftAlignedField(str, attr, procComm, 15);
- return;
- }
- case PROC_EXE: {
- const char* procExe;
- if (lp->procExe) {
- attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
- if (lp->procExeDeleted)
- attr = CRT_colors[FAILED_READ];
- procExe = lp->procExe + lp->procExeBasenameOffset;
+ break;
+ case AUTOGROUP_NICE:
+ 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];
} else {
attr = CRT_colors[PROCESS_SHADOW];
- procExe = Process_isKernelThread(lp) ? kthreadID : "N/A";
+ xSnprintf(buffer, n, "N/A ");
}
- Process_printLeftAlignedField(str, attr, procExe, 15);
- return;
- }
- case CWD: {
- const char* cwd;
- if (!lp->cwd) {
- attr = CRT_colors[PROCESS_SHADOW];
- cwd = "N/A";
- } else if (String_startsWith(lp->cwd, "/proc/") && strstr(lp->cwd, " (deleted)") != NULL) {
- attr = CRT_colors[PROCESS_SHADOW];
- cwd = "main thread terminated";
- } else {
- cwd = lp->cwd;
- }
- Process_printLeftAlignedField(str, attr, cwd, 25);
- return;
- }
+ break;
default:
Process_writeField(this, str, field);
return;
}
- RichString_appendWide(str, attr, buffer);
+ RichString_appendAscii(str, attr, buffer);
+}
+
+static double adjustNaN(double num) {
+ if (isnan(num))
+ return -0.0005;
+
+ return num;
}
static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
@@ -760,98 +317,84 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
switch (key) {
case M_DRS:
- return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs);
- case M_DT:
- return SPACESHIP_NUMBER(p2->m_dt, p1->m_dt);
+ return SPACESHIP_NUMBER(p1->m_drs, p2->m_drs);
case M_LRS:
- return SPACESHIP_NUMBER(p2->m_lrs, p1->m_lrs);
+ return SPACESHIP_NUMBER(p1->m_lrs, p2->m_lrs);
case M_TRS:
- return SPACESHIP_NUMBER(p2->m_trs, p1->m_trs);
+ return SPACESHIP_NUMBER(p1->m_trs, p2->m_trs);
case M_SHARE:
- return SPACESHIP_NUMBER(p2->m_share, p1->m_share);
+ return SPACESHIP_NUMBER(p1->m_share, p2->m_share);
case M_PSS:
- return SPACESHIP_NUMBER(p2->m_pss, p1->m_pss);
+ return SPACESHIP_NUMBER(p1->m_pss, p2->m_pss);
case M_SWAP:
- return SPACESHIP_NUMBER(p2->m_swap, p1->m_swap);
+ return SPACESHIP_NUMBER(p1->m_swap, p2->m_swap);
case M_PSSWP:
- return SPACESHIP_NUMBER(p2->m_psswp, p1->m_psswp);
+ return SPACESHIP_NUMBER(p1->m_psswp, p2->m_psswp);
case UTIME:
- return SPACESHIP_NUMBER(p2->utime, p1->utime);
+ return SPACESHIP_NUMBER(p1->utime, p2->utime);
case CUTIME:
- return SPACESHIP_NUMBER(p2->cutime, p1->cutime);
+ return SPACESHIP_NUMBER(p1->cutime, p2->cutime);
case STIME:
- return SPACESHIP_NUMBER(p2->stime, p1->stime);
+ return SPACESHIP_NUMBER(p1->stime, p2->stime);
case CSTIME:
- return SPACESHIP_NUMBER(p2->cstime, p1->cstime);
+ return SPACESHIP_NUMBER(p1->cstime, p2->cstime);
case RCHAR:
- return SPACESHIP_NUMBER(p2->io_rchar, p1->io_rchar);
+ return SPACESHIP_NUMBER(p1->io_rchar, p2->io_rchar);
case WCHAR:
- return SPACESHIP_NUMBER(p2->io_wchar, p1->io_wchar);
+ return SPACESHIP_NUMBER(p1->io_wchar, p2->io_wchar);
case SYSCR:
- return SPACESHIP_NUMBER(p2->io_syscr, p1->io_syscr);
+ return SPACESHIP_NUMBER(p1->io_syscr, p2->io_syscr);
case SYSCW:
- return SPACESHIP_NUMBER(p2->io_syscw, p1->io_syscw);
+ return SPACESHIP_NUMBER(p1->io_syscw, p2->io_syscw);
case RBYTES:
- return SPACESHIP_NUMBER(p2->io_read_bytes, p1->io_read_bytes);
+ return SPACESHIP_NUMBER(p1->io_read_bytes, p2->io_read_bytes);
case WBYTES:
- return SPACESHIP_NUMBER(p2->io_write_bytes, p1->io_write_bytes);
+ return SPACESHIP_NUMBER(p1->io_write_bytes, p2->io_write_bytes);
case CNCLWB:
- return SPACESHIP_NUMBER(p2->io_cancelled_write_bytes, p1->io_cancelled_write_bytes);
+ return SPACESHIP_NUMBER(p1->io_cancelled_write_bytes, p2->io_cancelled_write_bytes);
case IO_READ_RATE:
- return SPACESHIP_NUMBER(p2->io_rate_read_bps, p1->io_rate_read_bps);
+ return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_read_bps), adjustNaN(p2->io_rate_read_bps));
case IO_WRITE_RATE:
- return SPACESHIP_NUMBER(p2->io_rate_write_bps, p1->io_rate_write_bps);
+ return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_write_bps), adjustNaN(p2->io_rate_write_bps));
case IO_RATE:
- return SPACESHIP_NUMBER(p2->io_rate_read_bps + p2->io_rate_write_bps, p1->io_rate_read_bps + p1->io_rate_write_bps);
+ 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));
#ifdef HAVE_OPENVZ
case CTID:
return SPACESHIP_NULLSTR(p1->ctid, p2->ctid);
case VPID:
- return SPACESHIP_NUMBER(p2->vpid, p1->vpid);
+ return SPACESHIP_NUMBER(p1->vpid, p2->vpid);
#endif
#ifdef HAVE_VSERVER
case VXID:
- return SPACESHIP_NUMBER(p2->vxid, p1->vxid);
+ return SPACESHIP_NUMBER(p1->vxid, p2->vxid);
#endif
case CGROUP:
return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup);
case OOM:
- return SPACESHIP_NUMBER(p2->oom, p1->oom);
+ return SPACESHIP_NUMBER(p1->oom, p2->oom);
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY:
- return SPACESHIP_NUMBER(p2->cpu_delay_percent, p1->cpu_delay_percent);
+ return SPACESHIP_NUMBER(p1->cpu_delay_percent, p2->cpu_delay_percent);
case PERCENT_IO_DELAY:
- return SPACESHIP_NUMBER(p2->blkio_delay_percent, p1->blkio_delay_percent);
+ return SPACESHIP_NUMBER(p1->blkio_delay_percent, p2->blkio_delay_percent);
case PERCENT_SWAP_DELAY:
- return SPACESHIP_NUMBER(p2->swapin_delay_percent, p1->swapin_delay_percent);
+ return SPACESHIP_NUMBER(p1->swapin_delay_percent, p2->swapin_delay_percent);
#endif
case IO_PRIORITY:
return SPACESHIP_NUMBER(LinuxProcess_effectiveIOPriority(p1), LinuxProcess_effectiveIOPriority(p2));
case CTXT:
- return SPACESHIP_NUMBER(p2->ctxt_diff, p1->ctxt_diff);
+ return SPACESHIP_NUMBER(p1->ctxt_diff, p2->ctxt_diff);
case SECATTR:
return SPACESHIP_NULLSTR(p1->secattr, p2->secattr);
- case PROC_COMM: {
- const char *comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : "");
- const char *comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : "");
- return strcmp(comm1, comm2);
- }
- case PROC_EXE: {
- const char *exe1 = p1->procExe ? (p1->procExe + p1->procExeBasenameOffset) : (Process_isKernelThread(p1) ? kthreadID : "");
- const char *exe2 = p2->procExe ? (p2->procExe + p2->procExeBasenameOffset) : (Process_isKernelThread(p2) ? kthreadID : "");
- return strcmp(exe1, exe2);
- }
- case CWD:
- return SPACESHIP_NULLSTR(p1->cwd, p2->cwd);
+ case AUTOGROUP_ID:
+ return SPACESHIP_NUMBER(p1->autogroup_id, p2->autogroup_id);
+ case AUTOGROUP_NICE:
+ return SPACESHIP_NUMBER(p1->autogroup_nice, p2->autogroup_nice);
default:
return Process_compareByKey_Base(v1, v2, key);
}
}
-bool Process_isThread(const Process* this) {
- return (Process_isUserlandThread(this) || Process_isKernelThread(this));
-}
-
const ProcessClass LinuxProcess_class = {
.super = {
.extends = Class(Process),
@@ -860,6 +403,5 @@ const ProcessClass LinuxProcess_class = {
.compare = Process_compare
},
.writeField = LinuxProcess_writeField,
- .getCommandStr = LinuxProcess_getCommandStr,
.compareByKey = LinuxProcess_compareByKey
};
diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h
index 2e9a9d1..20399d9 100644
--- a/linux/LinuxProcess.h
+++ b/linux/LinuxProcess.h
@@ -11,8 +11,9 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep
#include <stdbool.h>
+#include <sys/types.h>
-#include "IOPriority.h"
+#include "linux/IOPriority.h"
#include "Object.h"
#include "Process.h"
#include "Settings.h"
@@ -27,45 +28,11 @@ in the source distribution for its full text.
#define PROCESS_FLAG_LINUX_CTXT 0x00004000
#define PROCESS_FLAG_LINUX_SECATTR 0x00008000
#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000
-#define PROCESS_FLAG_LINUX_CWD 0x00020000
#define PROCESS_FLAG_LINUX_DELAYACCT 0x00040000
-
-
-/* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It
- * contains the merged Command string, and the information needed by
- * LinuxProcess_writeCommand to color the string. str will be NULL for kernel
- * threads and zombies */
-typedef struct LinuxProcessMergedCommand_ {
- char *str; /* merged Command string */
- int maxLen; /* maximum expected length of Command string */
- int baseStart; /* basename's start offset */
- int baseEnd; /* basename's end offset */
- int commStart; /* comm's start offset */
- int commEnd; /* comm's end offset */
- int sep1; /* first field separator, used if non-zero */
- int sep2; /* second field separator, used if non-zero */
- bool separateComm; /* whether comm is a separate field */
- bool unmatchedExe; /* whether exe matched with cmdline */
- bool cmdlineChanged; /* whether cmdline changed */
- bool exeChanged; /* whether exe changed */
- bool commChanged; /* whether comm changed */
- bool prevMergeSet; /* whether showMergedCommand was set */
- bool prevPathSet; /* whether showProgramPath was set */
- bool prevCommSet; /* whether findCommInCmdline was set */
- bool prevCmdlineSet; /* whether findCommInCmdline was set */
-} LinuxProcessMergedCommand;
+#define PROCESS_FLAG_LINUX_AUTOGROUP 0x00080000
typedef struct LinuxProcess_ {
Process super;
- char *procComm;
- char *procExe;
- int procExeLen;
- int procExeBasenameOffset;
- bool procExeDeleted;
- int procCmdlineBasenameOffset;
- int procCmdlineBasenameEnd;
- LinuxProcessMergedCommand mergedCommand;
- bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
unsigned long int cmajflt;
@@ -80,18 +47,37 @@ typedef struct LinuxProcess_ {
long m_trs;
long m_drs;
long m_lrs;
- long m_dt;
+
+ /* Data read (in bytes) */
unsigned long long io_rchar;
+
+ /* Data written (in bytes) */
unsigned long long io_wchar;
+
+ /* Number of read(2) syscalls */
unsigned long long io_syscr;
+
+ /* Number of write(2) syscalls */
unsigned long long io_syscw;
+
+ /* Storage data read (in bytes) */
unsigned long long io_read_bytes;
+
+ /* Storage data written (in bytes) */
unsigned long long io_write_bytes;
+
+ /* Storage data cancelled (in bytes) */
unsigned long long io_cancelled_write_bytes;
- unsigned long long io_rate_read_time;
- unsigned long long io_rate_write_time;
+
+ /* Point in time of last io scan (in milliseconds elapsed since the Epoch) */
+ unsigned long long io_last_scan_time_ms;
+
+ /* Storage data read (in bytes per second) */
double io_rate_read_bps;
+
+ /* Storage data written (in bytes per second) */
double io_rate_write_bps;
+
#ifdef HAVE_OPENVZ
char* ctid;
pid_t vpid;
@@ -101,7 +87,6 @@ typedef struct LinuxProcess_ {
#endif
char* cgroup;
unsigned int oom;
- char* ttyDevice;
#ifdef HAVE_DELAYACCT
unsigned long long int delay_read_time;
unsigned long long cpu_delay_total;
@@ -115,14 +100,11 @@ typedef struct LinuxProcess_ {
unsigned long ctxt_diff;
char* secattr;
unsigned long long int last_mlrs_calctime;
- char* cwd;
-} LinuxProcess;
-
-#define Process_isKernelThread(_process) (((const LinuxProcess*)(_process))->isKernelThread)
-static inline bool Process_isUserlandThread(const Process* this) {
- return this->pid != this->tgid;
-}
+ /* Autogroup scheduling (CFS) information */
+ long int autogroup_id;
+ int autogroup_nice;
+} LinuxProcess;
extern int pageSize;
@@ -140,9 +122,9 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
bool LinuxProcess_setIOPriority(Process* this, Arg ioprio);
-/* This function constructs the string that is displayed by
- * LinuxProcess_writeCommand and also returned by LinuxProcess_getCommandStr */
-void LinuxProcess_makeCommandStr(Process *this);
+bool LinuxProcess_isAutogroupEnabled(void);
+
+bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta);
bool Process_isThread(const Process* this);
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index 434d070..8d82b00 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -7,7 +7,7 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep
-#include "LinuxProcessList.h"
+#include "linux/LinuxProcessList.h"
#include <assert.h>
#include <dirent.h>
@@ -20,9 +20,10 @@ in the source distribution for its full text.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <strings.h>
+#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
-#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_DELAYACCT
@@ -39,14 +40,15 @@ in the source distribution for its full text.
#include "Compat.h"
#include "CRT.h"
-#include "LinuxProcess.h"
#include "Macros.h"
#include "Object.h"
#include "Process.h"
#include "Settings.h"
#include "XUtils.h"
+#include "linux/LinuxProcess.h"
+#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep
-#ifdef MAJOR_IN_MKDEV
+#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
@@ -57,13 +59,7 @@ in the source distribution for its full text.
#endif
-// CentOS 6's kernel doesn't provide a definition of O_PATH
-// based on definition taken from uapi/asm-generic/fcnth.h in Linux kernel tree
-#ifndef O_PATH
-# define O_PATH 010000000
-#endif
-
-static long long btime;
+static long long btime = -1;
static long jiffy;
@@ -107,7 +103,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
while (*at != '\0') {
at = strchr(at, ' '); // skip first token
while (*at == ' ') at++; // skip spaces
- char* token = at; // mark beginning of path
+ const char* token = at; // mark beginning of path
at = strchr(at, ' '); // find end of path
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].path = xStrdup(token); // save
@@ -161,51 +157,90 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
#endif
-static int LinuxProcessList_computeCPUcount(void) {
- FILE* file = fopen(PROCSTATFILE, "r");
- if (file == NULL) {
- CRT_fatalError("Cannot open " PROCSTATFILE);
- }
+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
+ */
- int cpus = 0;
- char buffer[PROC_LINE_LENGTH + 1];
- while (fgets(buffer, sizeof(buffer), file)) {
- if (String_startsWith(buffer, "cpu")) {
- cpus++;
- }
+ LinuxProcessList* this = (LinuxProcessList*) super;
+ unsigned int existing = 0, active = 0;
+
+ DIR* dir = opendir("/sys/devices/system/cpu");
+ if (!dir) {
+ this->cpuData = xReallocArrayZero(this->cpuData, super->existingCPUs ? (super->existingCPUs + 1) : 0, 2, sizeof(CPUData));
+ this->cpuData[0].online = true; /* average is always "online" */
+ this->cpuData[1].online = true;
+ super->activeCPUs = 1;
+ super->existingCPUs = 1;
+ return;
}
- fclose(file);
+ unsigned int currExisting = super->existingCPUs;
- /* subtract raw cpu entry */
- if (cpus > 0) {
- cpus--;
- }
+ const struct dirent* entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type != DT_DIR)
+ continue;
- return cpus;
-}
+ if (!String_startsWith(entry->d_name, "cpu"))
+ continue;
-static void LinuxProcessList_updateCPUcount(LinuxProcessList* this) {
- ProcessList* pl = &(this->super);
- int cpus = LinuxProcessList_computeCPUcount();
- if (cpus == 0 || cpus == pl->cpuCount)
- return;
+ 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
- pl->cpuCount = cpus;
- free(this->cpus);
- this->cpus = xCalloc(cpus + 1, sizeof(CPUData));
+ existing++;
- for (int i = 0; i <= cpus; i++) {
- this->cpus[i].totalTime = 1;
- this->cpus[i].totalPeriod = 1;
+ /* 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);
+
+#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;
}
-ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
+ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
- ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId);
+ ProcessList_init(pl, Class(LinuxProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId);
LinuxProcessList_initTtyDrivers(this);
// Initialize page size
@@ -223,37 +258,27 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
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)
- CRT_fatalError("No btime in " PROCSTATFILE);
+ 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);
}
- // Initialize CPU count
- {
- int cpus = LinuxProcessList_computeCPUcount();
- pl->cpuCount = MAXIMUM(cpus, 1);
- this->cpus = xCalloc(cpus + 1, sizeof(CPUData));
+ fclose(statfile);
- for (int i = 0; i <= cpus; i++) {
- this->cpus[i].totalTime = 1;
- this->cpus[i].totalPeriod = 1;
- }
- }
+ if (btime == -1)
+ CRT_fatalError("No btime in " PROCSTATFILE);
+
+ // Initialize CPU count
+ LinuxProcessList_updateCPUcount(pl);
return pl;
}
@@ -261,7 +286,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* pl) {
LinuxProcessList* this = (LinuxProcessList*) pl;
ProcessList_done(pl);
- free(this->cpus);
+ free(this->cpuData);
if (this->ttyDrivers) {
for (int i = 0; this->ttyDrivers[i].path; i++) {
free(this->ttyDrivers[i].path);
@@ -281,91 +306,131 @@ static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long
return t * 100 / jiffy;
}
-static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, int* commLen) {
+static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
- const int commLenIn = *commLen;
- *commLen = 0;
char buf[MAX_READ + 1];
ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf));
if (r < 0)
return false;
+ /* (1) pid - %d */
assert(process->pid == atoi(buf));
char* location = strchr(buf, ' ');
if (!location)
return false;
+ /* (2) comm - (%s) */
location += 2;
char* end = strrchr(location, ')');
if (!end)
return false;
- int commsize = MINIMUM(end - location, commLenIn - 1);
- // deepcode ignore BufferOverflow: commsize is bounded by the allocated length passed in by commLen, saved into commLenIn
- memcpy(command, location, commsize);
- command[commsize] = '\0';
- *commLen = commsize;
+ String_safeStrncpy(command, location, MINIMUM((size_t)(end - location + 1), commLen));
+
location = end + 2;
+ /* (3) state - %c */
process->state = location[0];
location += 2;
+
+ /* (4) ppid - %d */
process->ppid = strtol(location, &location, 10);
location += 1;
- process->pgrp = strtoul(location, &location, 10);
+
+ /* (5) pgrp - %d */
+ process->pgrp = strtol(location, &location, 10);
location += 1;
- process->session = strtoul(location, &location, 10);
+
+ /* (6) session - %d */
+ process->session = strtol(location, &location, 10);
location += 1;
+
+ /* (7) tty_nr - %d */
process->tty_nr = strtoul(location, &location, 10);
location += 1;
+
+ /* (8) tpgid - %d */
process->tpgid = strtol(location, &location, 10);
location += 1;
- process->flags = strtoul(location, &location, 10);
- location += 1;
+
+ /* Skip (9) flags - %u */
+ location = strchr(location, ' ') + 1;
+
+ /* (10) minflt - %lu */
process->minflt = strtoull(location, &location, 10);
location += 1;
+
+ /* (11) cminflt - %lu */
lp->cminflt = strtoull(location, &location, 10);
location += 1;
+
+ /* (12) majflt - %lu */
process->majflt = strtoull(location, &location, 10);
location += 1;
+
+ /* (13) cmajflt - %lu */
lp->cmajflt = strtoull(location, &location, 10);
location += 1;
+
+ /* (14) utime - %lu */
lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
+
+ /* (15) stime - %lu */
lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
+
+ /* (16) cutime - %ld */
lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
+
+ /* (17) cstime - %ld */
lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
+
+ /* (18) priority - %ld */
process->priority = strtol(location, &location, 10);
location += 1;
+
+ /* (19) nice - %ld */
process->nice = strtol(location, &location, 10);
location += 1;
+
+ /* (20) num_threads - %ld */
process->nlwp = strtol(location, &location, 10);
location += 1;
+
+ /* Skip (21) itrealvalue - %ld */
location = strchr(location, ' ') + 1;
+
+ /* (22) starttime - %llu */
if (process->starttime_ctime == 0) {
process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100;
} else {
- location = strchr(location, ' ') + 1;
+ location = strchr(location, ' ');
}
location += 1;
- for (int i = 0; i < 15; i++) {
+
+ /* Skip (23) - (38) */
+ for (int i = 0; i < 16; i++) {
location = strchr(location, ' ') + 1;
}
- process->exit_signal = strtol(location, &location, 10);
- location += 1;
+
assert(location != NULL);
+
+ /* (39) processor - %d */
process->processor = strtol(location, &location, 10);
+ /* Ignore further fields */
+
process->time = lp->utime + lp->stime;
return true;
}
-static bool LinuxProcessList_statProcessDir(Process* process, openat_arg_t procFd) {
+static bool LinuxProcessList_updateUser(ProcessList* processList, Process* process, openat_arg_t procFd) {
struct stat sstat;
#ifdef HAVE_OPENAT
int statok = fstat(procFd, &sstat);
@@ -374,32 +439,36 @@ static bool LinuxProcessList_statProcessDir(Process* process, openat_arg_t procF
#endif
if (statok == -1)
return false;
- process->st_uid = sstat.st_uid;
+
+ if (process->st_uid != sstat.st_uid) {
+ process->st_uid = sstat.st_uid;
+ process->user = UsersTable_getRef(processList->usersTable, sstat.st_uid);
+ }
+
return true;
}
-static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long now) {
+static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long realtimeMs) {
char buffer[1024];
ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer));
if (r < 0) {
process->io_rate_read_bps = NAN;
process->io_rate_write_bps = NAN;
- process->io_rchar = -1LL;
- process->io_wchar = -1LL;
- process->io_syscr = -1LL;
- process->io_syscw = -1LL;
- process->io_read_bytes = -1LL;
- process->io_write_bytes = -1LL;
- process->io_cancelled_write_bytes = -1LL;
- process->io_rate_read_time = -1LL;
- process->io_rate_write_time = -1LL;
+ 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;
return;
}
unsigned long long last_read = process->io_read_bytes;
unsigned long long last_write = process->io_write_bytes;
char* buf = buffer;
- char* line = NULL;
+ const char* line;
while ((line = strsep(&buf, "\n")) != NULL) {
switch (line[0]) {
case 'r':
@@ -407,9 +476,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
process->io_rchar = strtoull(line + 7, NULL, 10);
} else if (String_startsWith(line + 1, "ead_bytes: ")) {
process->io_read_bytes = strtoull(line + 12, NULL, 10);
- process->io_rate_read_bps =
- ((double)(process->io_read_bytes - last_read)) / (((double)(now - process->io_rate_read_time)) / 1000);
- process->io_rate_read_time = now;
+ process->io_rate_read_bps = (process->io_read_bytes - last_read) * /*ms to s*/1000 / (realtimeMs - process->io_last_scan_time_ms);
}
break;
case 'w':
@@ -417,9 +484,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
process->io_wchar = strtoull(line + 7, NULL, 10);
} else if (String_startsWith(line + 1, "rite_bytes: ")) {
process->io_write_bytes = strtoull(line + 13, NULL, 10);
- process->io_rate_write_bps =
- ((double)(process->io_write_bytes - last_write)) / (((double)(now - process->io_rate_write_time)) / 1000);
- process->io_rate_write_time = now;
+ process->io_rate_write_bps = (process->io_write_bytes - last_write) * /*ms to s*/1000 / (realtimeMs - process->io_last_scan_time_ms);
}
break;
case 's':
@@ -435,14 +500,16 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
}
}
}
+
+ process->io_last_scan_time_ms = realtimeMs;
}
typedef struct LibraryData_ {
- uint64_t size;
- bool exec;
+ uint64_t size;
+ bool exec;
} LibraryData;
-static inline uint64_t fast_strtoull_dec(char **str, int maxlen) {
+static inline uint64_t fast_strtoull_dec(char** str, int maxlen) {
register uint64_t result = 0;
if (!maxlen)
@@ -457,7 +524,7 @@ static inline uint64_t fast_strtoull_dec(char **str, int maxlen) {
return result;
}
-static inline uint64_t fast_strtoull_hex(char **str, int maxlen) {
+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;
@@ -491,26 +558,32 @@ static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void*
if (!value)
return;
- LibraryData* v = (LibraryData *)value;
- uint64_t* d = (uint64_t *)data;
+ const LibraryData* v = (const LibraryData*)value;
+ uint64_t* d = (uint64_t*)data;
if (!v->exec)
return;
*d += v->size;
}
-static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
+static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd, bool calcSize, bool checkDeletedLib) {
+ Process* proc = (Process*)process;
+
+ proc->usesDeletedLib = false;
+
FILE* mapsfile = fopenat(procFd, "maps", "r");
if (!mapsfile)
- return 0;
+ return;
- Hashtable* ht = Hashtable_new(64, true);
+ Hashtable* ht = NULL;
+ if (calcSize)
+ ht = Hashtable_new(64, true);
char buffer[1024];
while (fgets(buffer, sizeof(buffer), mapsfile)) {
uint64_t map_start;
uint64_t map_end;
- char map_perm[5];
+ bool map_execute;
unsigned int map_devmaj;
unsigned int map_devmin;
uint64_t map_inode;
@@ -520,7 +593,7 @@ static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
continue;
// Parse format: "%Lx-%Lx %4s %x %2x:%2x %Ld"
- char *readptr = buffer;
+ char* readptr = buffer;
map_start = fast_strtoull_hex(&readptr, 16);
if ('-' != *readptr++)
@@ -530,8 +603,7 @@ static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
if (' ' != *readptr++)
continue;
- memcpy(map_perm, readptr, 4);
- map_perm[4] = '\0';
+ map_execute = (readptr[2] == 'x');
readptr += 4;
if (' ' != *readptr++)
continue;
@@ -557,61 +629,67 @@ static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
if (!map_inode)
continue;
- LibraryData* libdata = Hashtable_get(ht, map_inode);
- if (!libdata) {
- libdata = xCalloc(1, sizeof(LibraryData));
- Hashtable_put(ht, map_inode, libdata);
+ if (calcSize) {
+ LibraryData* libdata = Hashtable_get(ht, map_inode);
+ if (!libdata) {
+ libdata = xCalloc(1, sizeof(LibraryData));
+ Hashtable_put(ht, map_inode, libdata);
+ }
+
+ libdata->size += map_end - map_start;
+ libdata->exec |= map_execute;
}
- libdata->size += map_end - map_start;
- libdata->exec |= 'x' == map_perm[2];
+ if (checkDeletedLib && map_execute && !proc->usesDeletedLib) {
+ while (*readptr == ' ')
+ readptr++;
+
+ if (*readptr != '/')
+ continue;
+
+ if (String_startsWith(readptr, "/memfd:"))
+ continue;
+
+ if (strstr(readptr, " (deleted)\n")) {
+ proc->usesDeletedLib = true;
+ if (!calcSize)
+ break;
+ }
+ }
}
fclose(mapsfile);
- uint64_t total_size = 0;
- Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size);
+ if (calcSize) {
+ uint64_t total_size = 0;
+ Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size);
- Hashtable_delete(ht);
+ Hashtable_delete(ht);
- return total_size / pageSize;
+ process->m_lrs = total_size / pageSize;
+ }
}
-static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) {
+static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd) {
FILE* statmfile = fopenat(procFd, "statm", "r");
if (!statmfile)
return false;
- long tmp_m_lrs = 0;
+ long int dummy, dummy2;
+
int r = fscanf(statmfile, "%ld %ld %ld %ld %ld %ld %ld",
&process->super.m_virt,
&process->super.m_resident,
&process->m_share,
&process->m_trs,
- &tmp_m_lrs,
+ &dummy, /* unused since Linux 2.6; always 0 */
&process->m_drs,
- &process->m_dt);
+ &dummy2); /* unused since Linux 2.6; always 0 */
fclose(statmfile);
if (r == 7) {
process->super.m_virt *= pageSizeKB;
process->super.m_resident *= pageSizeKB;
-
- if (tmp_m_lrs) {
- process->m_lrs = tmp_m_lrs;
- } else if (performLookup) {
- // Check if we really should recalculate the M_LRS value for this process
- uint64_t passedTimeInMs = now - process->last_mlrs_calctime;
-
- uint64_t recheck = ((uint64_t)rand()) % 2048;
-
- if(passedTimeInMs > 2000 || passedTimeInMs > recheck) {
- process->last_mlrs_calctime = now;
- process->m_lrs = LinuxProcessList_calcLibSize(procFd);
- }
- } else {
- // Keep previous value
- }
}
return r == 7;
@@ -656,7 +734,7 @@ static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t p
#ifdef HAVE_OPENVZ
static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t procFd) {
- if ( (access(PROCDIR "/vz", R_OK) != 0)) {
+ if (access(PROCDIR "/vz", R_OK) != 0) {
free(process->ctid);
process->ctid = NULL;
process->vpid = process->super.pid;
@@ -718,10 +796,8 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t
switch(field) {
case 1:
foundEnvID = true;
- if (!String_eq(name_value_sep, process->ctid ? process->ctid : "")) {
- free(process->ctid);
- process->ctid = xStrdup(name_value_sep);
- }
+ if (!String_eq(name_value_sep, process->ctid ? process->ctid : ""))
+ free_and_xStrdup(&process->ctid, name_value_sep);
break;
case 2:
foundVPid = true;
@@ -762,7 +838,7 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
int left = PROC_LINE_LENGTH;
while (!feof(file) && left > 0) {
char buffer[PROC_LINE_LENGTH + 1];
- char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
+ const char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
if (!ok)
break;
@@ -779,8 +855,7 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
left -= wrote;
}
fclose(file);
- free(process->cgroup);
- process->cgroup = xStrdup(output);
+ free_and_xStrdup(&process->cgroup, output);
}
#ifdef HAVE_VSERVER
@@ -831,6 +906,23 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t pro
fclose(file);
}
+static void LinuxProcessList_readAutogroup(LinuxProcess* process, openat_arg_t procFd) {
+ process->autogroup_id = -1;
+
+ char autogroup[64]; // space for two numeric values and fixed length strings
+ ssize_t amtRead = xReadfileat(procFd, "autogroup", autogroup, sizeof(autogroup));
+ if (amtRead < 0)
+ return;
+
+ long int identity;
+ int nice;
+ int ok = sscanf(autogroup, "/autogroup-%ld nice %d", &identity, &nice);
+ if (ok == 2) {
+ process->autogroup_id = identity;
+ process->autogroup_nice = nice;
+ }
+}
+
static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) {
FILE* file = fopenat(procFd, "status", "r");
if (!file)
@@ -867,7 +959,7 @@ static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t
}
char buffer[PROC_LINE_LENGTH + 1];
- char* res = fgets(buffer, sizeof(buffer), file);
+ const char* res = fgets(buffer, sizeof(buffer), file);
fclose(file);
if (!res) {
free(process->secattr);
@@ -881,8 +973,7 @@ static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t
if (process->secattr && String_eq(process->secattr, buffer)) {
return;
}
- free(process->secattr);
- process->secattr = xStrdup(buffer);
+ free_and_xStrdup(&process->secattr, buffer);
}
static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) {
@@ -897,18 +988,17 @@ static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd)
#endif
if (r < 0) {
- free(process->cwd);
- process->cwd = NULL;
+ free(process->super.procCwd);
+ process->super.procCwd = NULL;
return;
}
pathBuffer[r] = '\0';
- if (process->cwd && String_eq(process->cwd, pathBuffer))
+ if (process->super.procCwd && String_eq(process->super.procCwd, pathBuffer))
return;
- free(process->cwd);
- process->cwd = xStrdup(pathBuffer);
+ free_and_xStrdup(&process->super.procCwd, pathBuffer);
}
#ifdef HAVE_DELAYACCT
@@ -916,7 +1006,7 @@ static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd)
static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
struct nlmsghdr* nlhdr;
struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
- struct nlattr* nlattr;
+ const struct nlattr* nlattr;
struct taskstats stats;
int rem;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;
@@ -952,10 +1042,10 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
struct nl_msg* msg;
if (!this->netlink_socket) {
- LinuxProcessList_initNetlinkSocket(this);
- if (!this->netlink_socket) {
- goto delayacct_failure;
- }
+ LinuxProcessList_initNetlinkSocket(this);
+ if (!this->netlink_socket) {
+ goto delayacct_failure;
+ }
}
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
@@ -992,16 +1082,6 @@ delayacct_failure:
#endif
-static void setCommand(Process* process, const char* command, int len) {
- if (process->comm && process->commLen >= len) {
- strncpy(process->comm, command, len + 1);
- } else {
- free(process->comm);
- process->comm = xStrdup(command);
- }
- process->commLen = len;
-}
-
static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) {
char command[4096 + 1]; // max cmdline length on Linux
ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command));
@@ -1009,11 +1089,10 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
return false;
if (amtRead == 0) {
- if (process->state == 'Z') {
- process->basenameOffset = 0;
- } else {
- ((LinuxProcess*)process)->isKernelThread = true;
+ if (process->state != 'Z') {
+ process->isKernelThread = true;
}
+ Process_updateCmdline(process, NULL, 0, 0);
return true;
}
@@ -1025,7 +1104,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
for (int i = 0; i < amtRead; i++) {
/* newline used as delimiter - when forming the mergedCommand, newline is
- * converted to space by LinuxProcess_makeCommandStr */
+ * converted to space by Process_makeCommandStr */
if (command[i] == '\0') {
command[i] = '\n';
} else {
@@ -1121,35 +1200,27 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
}
}
}
+
+ /* Some command lines are hard to parse, like
+ * file.so [kdeinit5] file local:/run/user/1000/klauncherdqbouY.1.slave-socket local:/run/user/1000/kded5TwsDAx.1.slave-socket
+ * Reset if start is behind end.
+ */
+ if (tokenStart >= tokenEnd)
+ tokenStart = tokenEnd = 0;
}
if (tokenEnd == 0) {
tokenEnd = lastChar + 1;
}
- LinuxProcess *lp = (LinuxProcess *)process;
- lp->mergedCommand.maxLen = lastChar + 1; /* accommodate cmdline */
- if (!process->comm || !String_eq(command, process->comm)) {
- process->basenameOffset = tokenEnd;
- setCommand(process, command, lastChar + 1);
- lp->procCmdlineBasenameOffset = tokenStart;
- lp->procCmdlineBasenameEnd = tokenEnd;
- lp->mergedCommand.cmdlineChanged = true;
- }
+ Process_updateCmdline(process, command, tokenStart, tokenEnd);
/* /proc/[pid]/comm could change, so should be updated */
if ((amtRead = xReadfileat(procFd, "comm", command, sizeof(command))) > 0) {
command[amtRead - 1] = '\0';
- lp->mergedCommand.maxLen += amtRead - 1; /* accommodate comm */
- if (!lp->procComm || !String_eq(command, lp->procComm)) {
- free(lp->procComm);
- lp->procComm = xStrdup(command);
- lp->mergedCommand.commChanged = true;
- }
- } else if (lp->procComm) {
- free(lp->procComm);
- lp->procComm = NULL;
- lp->mergedCommand.commChanged = true;
+ Process_updateComm(process, command);
+ } else {
+ Process_updateComm(process, NULL);
}
char filename[MAX_NAME + 1];
@@ -1164,39 +1235,36 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
#endif
if (amtRead > 0) {
filename[amtRead] = 0;
- lp->mergedCommand.maxLen += amtRead; /* accommodate exe */
- if (!lp->procExe || !String_eq(filename, lp->procExe)) {
- free(lp->procExe);
- lp->procExe = xStrdup(filename);
- lp->procExeLen = amtRead;
- /* exe is guaranteed to contain at least one /, but validate anyway */
- while (amtRead && filename[--amtRead] != '/')
- ;
- lp->procExeBasenameOffset = amtRead + 1;
- lp->mergedCommand.exeChanged = true;
+ if (!process->procExe ||
+ (!process->procExeDeleted && !String_eq(filename, process->procExe)) ||
+ (process->procExeDeleted && !String_startsWith(filename, process->procExe))) {
const char* deletedMarker = " (deleted)";
- if (strlen(lp->procExe) > strlen(deletedMarker)) {
- lp->procExeDeleted = String_eq(lp->procExe + strlen(lp->procExe) - strlen(deletedMarker), deletedMarker);
+ const size_t markerLen = strlen(deletedMarker);
+ const size_t filenameLen = strlen(filename);
- if (lp->procExeDeleted && strlen(lp->procExe) - strlen(deletedMarker) == 1 && lp->procExe[0] == '/') {
- lp->procExeBasenameOffset = 0;
- }
+ if (filenameLen > markerLen) {
+ bool oldExeDeleted = process->procExeDeleted;
+
+ process->procExeDeleted = String_eq(filename + filenameLen - markerLen, deletedMarker);
+
+ if (process->procExeDeleted)
+ filename[filenameLen - markerLen] = '\0';
+
+ process->mergedCommand.exeChanged |= oldExeDeleted ^ process->procExeDeleted;
}
+
+ Process_updateExe(process, filename);
}
- } else if (lp->procExe) {
- free(lp->procExe);
- lp->procExe = NULL;
- lp->procExeLen = 0;
- lp->procExeBasenameOffset = 0;
- lp->procExeDeleted = false;
- lp->mergedCommand.exeChanged = true;
+ } else if (process->procExe) {
+ Process_updateExe(process, NULL);
+ process->procExeDeleted = false;
}
return true;
}
-static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned int tty_nr) {
+static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned long int tty_nr) {
unsigned int maj = major(tty_nr);
unsigned int min = minor(tty_nr);
@@ -1249,7 +1317,7 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned in
return out;
}
-static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period, unsigned long long now) {
+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;
@@ -1269,9 +1337,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
return false;
}
- int cpus = pl->cpuCount;
- bool hideKernelThreads = settings->hideKernelThreads;
- bool hideUserlandThreads = settings->hideUserlandThreads;
+ const unsigned int activeCPUs = pl->activeCPUs;
+ const bool hideKernelThreads = settings->hideKernelThreads;
+ const bool hideUserlandThreads = settings->hideUserlandThreads;
while ((entry = readdir(dir)) != NULL) {
const char* name = entry->d_name;
@@ -1292,11 +1360,16 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
// filename is a number: process directory
- int pid = atoi(name);
-
- if (pid <= 0)
- continue;
+ int pid;
+ {
+ char* endptr;
+ unsigned long parsedPid = strtoul(name, &endptr, 10);
+ if (parsedPid == 0 || parsedPid == ULONG_MAX || *endptr != '\0')
+ continue;
+ pid = parsedPid;
+ }
+ // Skip task directory of main thread
if (parent && pid == parent->pid)
continue;
@@ -1305,9 +1378,10 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
LinuxProcess* lp = (LinuxProcess*) proc;
proc->tgid = parent ? parent->pid : pid;
+ proc->isUserlandThread = proc->pid != proc->tgid;
#ifdef HAVE_OPENAT
- int procFd = openat(dirFd, entry->d_name, O_PATH | O_DIRECTORY | O_NOFOLLOW);
+ int procFd = openat(dirFd, entry->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (procFd < 0)
goto errorReadingProcess;
#else
@@ -1315,7 +1389,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
#endif
- LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period, now);
+ LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period);
/*
* These conditions will not trigger on first occurrence, cause we need to
@@ -1341,11 +1415,33 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
if (settings->flags & PROCESS_FLAG_IO)
- LinuxProcessList_readIoFile(lp, procFd, now);
+ LinuxProcessList_readIoFile(lp, procFd, pl->realtimeMs);
- if (!LinuxProcessList_readStatmFile(lp, procFd, !!(settings->flags & PROCESS_FLAG_LINUX_LRS_FIX), now))
+ if (!LinuxProcessList_readStatmFile(lp, procFd))
goto errorReadingProcess;
+ {
+ bool prev = proc->usesDeletedLib;
+
+ if ((settings->flags & PROCESS_FLAG_LINUX_LRS_FIX) ||
+ (settings->highlightDeletedExe && !proc->procExeDeleted && !proc->isKernelThread && !proc->isUserlandThread)) {
+ // Check if we really should recalculate the M_LRS value for this process
+ uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime;
+
+ uint64_t recheck = ((uint64_t)rand()) % 2048;
+
+ if (passedTimeInMs > recheck) {
+ lp->last_mlrs_calctime = pl->realtimeMs;
+ LinuxProcessList_readMaps(lp, procFd, settings->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe);
+ }
+ } else {
+ /* Copy from process structure in threads and reset if setting got disabled */
+ proc->usesDeletedLib = (proc->isUserlandThread && parent) ? parent->usesDeletedLib : false;
+ }
+
+ proc->mergedCommand.exeChanged |= prev ^ proc->usesDeletedLib;
+ }
+
if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {
if (!parent) {
// Read smaps file of each process only every second pass to improve performance
@@ -1361,16 +1457,15 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
}
- char command[MAX_NAME + 1];
+ char statCommand[MAX_NAME + 1];
unsigned long long int lasttimes = (lp->utime + lp->stime);
- int commLen = sizeof(command);
- unsigned int tty_nr = proc->tty_nr;
- if (! LinuxProcessList_readStatFile(proc, procFd, command, &commLen))
+ unsigned long int tty_nr = proc->tty_nr;
+ if (! LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand)))
goto errorReadingProcess;
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
- free(lp->ttyDevice);
- lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
+ free(proc->tty_name);
+ proc->tty_name = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO) {
@@ -1379,15 +1474,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
/* period might be 0 after system sleep */
float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
- proc->percent_cpu = CLAMP(percent_cpu, 0.0F, cpus * 100.0F);
+ proc->percent_cpu = CLAMP(percent_cpu, 0.0F, activeCPUs * 100.0F);
proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0;
- if (!preExisting) {
-
- if (! LinuxProcessList_statProcessDir(proc, procFd))
- goto errorReadingProcess;
+ if (! LinuxProcessList_updateUser(pl, proc, procFd))
+ goto errorReadingProcess;
- proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
+ if (!preExisting) {
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
@@ -1415,15 +1508,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
}
}
- /* (Re)Generate the Command string, but only if the process is:
- * - not a kernel thread, and
- * - not a zombie or it became zombie under htop's watch, and
- * - not a user thread or if showThreadNames is not set */
- if (!Process_isKernelThread(proc) &&
- (proc->state != 'Z' || lp->mergedCommand.str) &&
- (!Process_isUserlandThread(proc) || !settings->showThreadNames)) {
- LinuxProcess_makeCommandStr(proc);
- }
#ifdef HAVE_DELAYACCT
if (settings->flags & PROCESS_FLAG_LINUX_DELAYACCT) {
@@ -1447,35 +1531,30 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
LinuxProcessList_readSecattrData(lp, procFd);
}
- if (settings->flags & PROCESS_FLAG_LINUX_CWD) {
+ if (settings->flags & PROCESS_FLAG_CWD) {
LinuxProcessList_readCwd(lp, procFd);
}
- if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
- proc->basenameOffset = -1;
- setCommand(proc, command, commLen);
- } else if (Process_isThread(proc)) {
- if (settings->showThreadNames || Process_isKernelThread(proc) || (proc->state == 'Z' && proc->basenameOffset == 0)) {
- proc->basenameOffset = -1;
- setCommand(proc, command, commLen);
- } else if (settings->showThreadNames) {
- if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
- goto errorReadingProcess;
- }
- }
- if (Process_isKernelThread(proc)) {
- pl->kernelThreads++;
- } else {
- pl->userlandThreads++;
- }
+ if ((settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) {
+ LinuxProcessList_readAutogroup(lp, procFd);
+ }
+
+ if (!proc->cmdline && statCommand[0] &&
+ (proc->state == 'Z' || Process_isKernelThread(proc) || settings->showThreadNames)) {
+ Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
+ }
+
+ if (Process_isKernelThread(proc)) {
+ pl->kernelThreads++;
+ } else if (Process_isUserlandThread(proc)) {
+ pl->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)));
pl->totalTasks++;
- if (proc->state == 'R')
- pl->runningTasks++;
+ /* runningTasks is set in LinuxProcessList_scanCPUTime() from /proc/stat */
proc->updated = true;
Compat_openatArgClose(procFd);
continue;
@@ -1501,63 +1580,147 @@ errorReadingProcess:
}
static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
- unsigned long long int freeMem = 0;
- unsigned long long int swapFree = 0;
- unsigned long long int shmem = 0;
- unsigned long long int sreclaimable = 0;
+ 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 == NULL) {
+ if (!file)
CRT_fatalError("Cannot open " PROCMEMINFOFILE);
- }
+
char buffer[128];
- while (fgets(buffer, 128, file)) {
+ while (fgets(buffer, sizeof(buffer), file)) {
- #define tryRead(label, variable) \
- if (String_startsWith(buffer, label)) { \
- sscanf(buffer + strlen(label), " %32llu kB", variable); \
- break; \
+ #define tryRead(label, variable) \
+ if (String_startsWith(buffer, label)) { \
+ memory_t parsed_; \
+ if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \
+ (variable) = parsed_; \
+ } \
+ break; \
}
switch (buffer[0]) {
case 'M':
- tryRead("MemTotal:", &this->totalMem);
- tryRead("MemFree:", &freeMem);
+ tryRead("MemAvailable:", availableMem);
+ tryRead("MemFree:", freeMem);
+ tryRead("MemTotal:", totalMem);
break;
case 'B':
- tryRead("Buffers:", &this->buffersMem);
+ tryRead("Buffers:", buffersMem);
break;
case 'C':
- tryRead("Cached:", &this->cachedMem);
+ tryRead("Cached:", cachedMem);
break;
case 'S':
switch (buffer[1]) {
- case 'w':
- tryRead("SwapTotal:", &this->totalSwap);
- tryRead("SwapFree:", &swapFree);
- break;
case 'h':
- tryRead("Shmem:", &shmem);
+ tryRead("Shmem:", sharedMem);
+ break;
+ case 'w':
+ tryRead("SwapTotal:", swapTotalMem);
+ tryRead("SwapCached:", swapCacheMem);
+ tryRead("SwapFree:", swapFreeMem);
break;
case 'R':
- tryRead("SReclaimable:", &sreclaimable);
+ tryRead("SReclaimable:", sreclaimableMem);
break;
}
break;
}
+
#undef tryRead
}
- this->usedMem = this->totalMem - freeMem;
- this->cachedMem = this->cachedMem + sreclaimable - shmem;
- this->usedSwap = this->totalSwap - swapFree;
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) {
- unsigned long long int totalZram = 0;
- unsigned long long int usedZramComp = 0;
- unsigned long long int usedZramOrig = 0;
+ memory_t totalZram = 0;
+ memory_t usedZramComp = 0;
+ memory_t usedZramOrig = 0;
char mm_stat[34];
char disksize[34];
@@ -1578,9 +1741,9 @@ static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) {
}
break;
}
- unsigned long long int size = 0;
- unsigned long long int orig_data_size = 0;
- unsigned long long int compr_data_size = 0;
+ 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)) {
@@ -1603,9 +1766,9 @@ static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) {
}
static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
- unsigned long long int dbufSize = 0;
- unsigned long long int dnodeSize = 0;
- unsigned long long int bonusSize = 0;
+ memory_t dbufSize = 0;
+ memory_t dnodeSize = 0;
+ memory_t bonusSize = 0;
FILE* file = fopen(PROCARCSTATSFILE, "r");
if (file == NULL) {
@@ -1673,61 +1836,78 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
}
}
-static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
+static inline double LinuxProcessList_scanCPUTime(ProcessList* super) {
+ LinuxProcessList* this = (LinuxProcessList*) super;
+
+ LinuxProcessList_updateCPUcount(super);
FILE* file = fopen(PROCSTATFILE, "r");
- if (file == NULL) {
+ if (!file)
CRT_fatalError("Cannot open " PROCSTATFILE);
- }
- int cpus = this->super.cpuCount;
- assert(cpus > 0);
- for (int i = 0; i <= cpus; i++) {
+
+ 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, irq, softIrq, steal, guest, guestnice;
- ioWait = irq = softIrq = steal = guest = guestnice = 0;
+ 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.
- char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
- if (!ok) {
- buffer[0] = '\0';
- }
-
+ 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 {
- int cpuid;
- (void) sscanf(buffer, "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
- assert(cpuid == i - 1);
+ 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 = usertime - guest;
- nicetime = nicetime - guestnice;
+ 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->cpus[i]);
+ 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.
- #define WRAP_SUBTRACT(a,b) (((a) > (b)) ? (a) - (b) : 0)
- cpuData->userPeriod = WRAP_SUBTRACT(usertime, cpuData->userTime);
- cpuData->nicePeriod = WRAP_SUBTRACT(nicetime, cpuData->niceTime);
- cpuData->systemPeriod = WRAP_SUBTRACT(systemtime, cpuData->systemTime);
- cpuData->systemAllPeriod = WRAP_SUBTRACT(systemalltime, cpuData->systemAllTime);
- cpuData->idleAllPeriod = WRAP_SUBTRACT(idlealltime, cpuData->idleAllTime);
- cpuData->idlePeriod = WRAP_SUBTRACT(idletime, cpuData->idleTime);
- cpuData->ioWaitPeriod = WRAP_SUBTRACT(ioWait, cpuData->ioWaitTime);
- cpuData->irqPeriod = WRAP_SUBTRACT(irq, cpuData->irqTime);
- cpuData->softIrqPeriod = WRAP_SUBTRACT(softIrq, cpuData->softIrqTime);
- cpuData->stealPeriod = WRAP_SUBTRACT(steal, cpuData->stealTime);
- cpuData->guestPeriod = WRAP_SUBTRACT(virtalltime, cpuData->guestTime);
- cpuData->totalPeriod = WRAP_SUBTRACT(totaltime, cpuData->totalTime);
- #undef WRAP_SUBTRACT
+ 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;
@@ -1742,19 +1922,50 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
cpuData->totalTime = totaltime;
}
- double period = (double)this->cpus[0].totalPeriod / cpus;
+ 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 scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) {
- int cpus = this->super.cpuCount;
+ unsigned int existingCPUs = this->super.existingCPUs;
int numCPUsWithFrequency = 0;
unsigned long totalFrequency = 0;
- for (int i = 0; i < cpus; ++i) {
+ /*
+ * 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%d/cpufreq/scaling_cur_freq", i);
+ 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)
@@ -1764,16 +1975,27 @@ static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) {
if (fscanf(file, "%lu", &frequency) == 1) {
/* convert kHz to MHz */
frequency = frequency / 1000;
- this->cpus[i + 1].frequency = frequency;
+ 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->cpus[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
+ this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
return 0;
}
@@ -1783,7 +2005,7 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
if (file == NULL)
return;
- int cpus = this->super.cpuCount;
+ unsigned int existingCPUs = this->super.existingCPUs;
int numCPUsWithFrequency = 0;
double totalFrequency = 0;
int cpuid = -1;
@@ -1806,11 +2028,11 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
(sscanf(buffer, "clock : %lfMHz", &frequency) == 1) ||
(sscanf(buffer, "clock: %lfMHz", &frequency) == 1)
) {
- if (cpuid < 0 || cpuid > (cpus - 1)) {
+ if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) {
continue;
}
- CPUData* cpuData = &(this->cpus[cpuid + 1]);
+ CPUData* cpuData = &(this->cpuData[cpuid + 1]);
/* do not override sysfs data */
if (isnan(cpuData->frequency)) {
cpuData->frequency = frequency;
@@ -1824,16 +2046,15 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
fclose(file);
if (numCPUsWithFrequency > 0) {
- this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency;
+ this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency;
}
}
static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
- int cpus = this->super.cpuCount;
- assert(cpus > 0);
+ unsigned int existingCPUs = this->super.existingCPUs;
- for (int i = 0; i <= cpus; i++) {
- this->cpus[i].frequency = NAN;
+ for (unsigned int i = 0; i <= existingCPUs; i++) {
+ this->cpuData[i].frequency = NAN;
}
if (scanCPUFreqencyFromSysCPUFreq(this) == 0) {
@@ -1848,11 +2069,11 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
const Settings* settings = super->settings;
LinuxProcessList_scanMemoryInfo(super);
+ LinuxProcessList_scanHugePages(this);
LinuxProcessList_scanZfsArcstats(this);
- LinuxProcessList_updateCPUcount(this);
LinuxProcessList_scanZramInfo(this);
- double period = LinuxProcessList_scanCPUTime(this);
+ double period = LinuxProcessList_scanCPUTime(super);
if (settings->showCPUFrequency) {
LinuxProcessList_scanCPUFrequency(this);
@@ -1860,7 +2081,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
#ifdef HAVE_SENSORS_SENSORS_H
if (settings->showCPUTemperature)
- LibSensors_getCPUTemperatures(this->cpus, this->super.cpuCount);
+ LibSensors_getCPUTemperatures(this->cpuData, this->super.existingCPUs, this->super.activeCPUs);
#endif
// in pause mode only gather global data for meters (CPU/memory/...)
@@ -1868,9 +2089,15 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
return;
}
- struct timeval tv;
- gettimeofday(&tv, NULL);
- unsigned long long now = tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
+ if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP) {
+ // Refer to sched(7) 'autogroup feature' section
+ // The kernel feature can be enabled/disabled through procfs at
+ // any time, so check for it at the start of each sample - only
+ // read from per-process procfs files if it's globally enabled.
+ this->haveAutogroup = LinuxProcess_isAutogroupEnabled();
+ } else {
+ this->haveAutogroup = false;
+ }
/* PROCDIR is an absolute path */
assert(PROCDIR[0] == '/');
@@ -1880,5 +2107,12 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
openat_arg_t rootFd = "";
#endif
- LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period, now);
+ 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;
}
diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h
index 09b84af..a5640e2 100644
--- a/linux/LinuxProcessList.h
+++ b/linux/LinuxProcessList.h
@@ -18,6 +18,8 @@ in the source distribution for its full text.
#include "ZramStats.h"
#include "zfs/ZfsArcStats.h"
+#define HTOP_HUGEPAGE_BASE_SHIFT 16
+#define HTOP_HUGEPAGE_COUNT 24
typedef struct CPUData_ {
unsigned long long int totalTime;
@@ -51,6 +53,8 @@ typedef struct CPUData_ {
#ifdef HAVE_SENSORS_SENSORS_H
double temperature;
#endif
+
+ bool online;
} CPUData;
typedef struct TtyDriver_ {
@@ -63,15 +67,22 @@ typedef struct TtyDriver_ {
typedef struct LinuxProcessList_ {
ProcessList super;
- CPUData* cpus;
+ CPUData* cpuData;
+
TtyDriver* ttyDrivers;
bool haveSmapsRollup;
+ bool haveAutogroup;
#ifdef HAVE_DELAYACCT
struct nl_sock* netlink_socket;
int netlink_family;
#endif
+ memory_t totalHugePageMem;
+ memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT];
+
+ memory_t availableMem;
+
ZfsArcStats zfs;
ZramStats zram;
} LinuxProcessList;
@@ -104,10 +115,12 @@ typedef struct LinuxProcessList_ {
#define PROC_LINE_LENGTH 4096
#endif
-ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
+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/Platform.c b/linux/Platform.c
index dd80ded..05023d5 100644
--- a/linux/Platform.c
+++ b/linux/Platform.c
@@ -7,14 +7,13 @@ in the source distribution for its full text.
#include "config.h"
-#include "Platform.h"
+#include "linux/Platform.h"
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <limits.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
@@ -31,40 +30,55 @@ in the source distribution for its full text.
#include "DateTimeMeter.h"
#include "DiskIOMeter.h"
#include "HostnameMeter.h"
-#include "IOPriority.h"
-#include "IOPriorityPanel.h"
-#include "LinuxProcess.h"
-#include "LinuxProcessList.h"
+#include "HugePageMeter.h"
#include "LoadAverageMeter.h"
#include "Macros.h"
#include "MainPanel.h"
#include "Meter.h"
#include "MemoryMeter.h"
+#include "MemorySwapMeter.h"
#include "NetworkIOMeter.h"
#include "Object.h"
#include "Panel.h"
#include "PressureStallMeter.h"
#include "ProcessList.h"
#include "ProvideCurses.h"
-#include "SELinuxMeter.h"
+#include "linux/SELinuxMeter.h"
#include "Settings.h"
#include "SwapMeter.h"
-#include "SystemdMeter.h"
+#include "SysArchMeter.h"
#include "TasksMeter.h"
#include "UptimeMeter.h"
#include "XUtils.h"
-#include "ZramMeter.h"
-#include "ZramStats.h"
-
+#include "linux/IOPriority.h"
+#include "linux/IOPriorityPanel.h"
+#include "linux/LinuxProcess.h"
+#include "linux/LinuxProcessList.h"
+#include "linux/SystemdMeter.h"
+#include "linux/ZramMeter.h"
+#include "linux/ZramStats.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/ZfsCompressedArcMeter.h"
+#ifdef HAVE_LIBCAP
+#include <errno.h>
+#include <sys/capability.h>
+#endif
+
#ifdef HAVE_SENSORS_SENSORS_H
#include "LibSensors.h"
#endif
+#ifdef HAVE_LIBCAP
+enum CapMode {
+ CAP_MODE_OFF,
+ CAP_MODE_BASIC,
+ CAP_MODE_STRICT
+};
+#endif
+
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = {
@@ -111,36 +125,24 @@ static time_t Platform_Battery_cacheTime;
static double Platform_Battery_cachePercent = NAN;
static ACPresence Platform_Battery_cacheIsOnAC;
-void Platform_init(void) {
- if (access(PROCDIR, R_OK) != 0) {
- fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
- exit(1);
- }
-
-#ifdef HAVE_SENSORS_SENSORS_H
- LibSensors_init(NULL);
-#endif
-}
-
-void Platform_done(void) {
-#ifdef HAVE_SENSORS_SENSORS_H
- LibSensors_cleanup();
+#ifdef HAVE_LIBCAP
+static enum CapMode Platform_capabilitiesMode = CAP_MODE_BASIC;
#endif
-}
static Htop_Reaction Platform_actionSetIOPriority(State* st) {
- Panel* panel = st->panel;
+ if (Settings_isReadonly())
+ return HTOP_OK;
- LinuxProcess* p = (LinuxProcess*) Panel_getSelected(panel);
+ const LinuxProcess* p = (const LinuxProcess*) Panel_getSelected((Panel*)st->mainPanel);
if (!p)
return HTOP_OK;
IOPriority ioprio1 = p->ioPriority;
Panel* ioprioPanel = IOPriorityPanel_new(ioprio1);
- void* set = Action_pickFromVector(st, ioprioPanel, 21, true);
+ const void* set = Action_pickFromVector(st, ioprioPanel, 20, true);
if (set) {
IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel);
- bool ok = MainPanel_foreachProcess((MainPanel*)panel, LinuxProcess_setIOPriority, (Arg) { .i = ioprio2 }, NULL);
+ bool ok = MainPanel_foreachProcess(st->mainPanel, LinuxProcess_setIOPriority, (Arg) { .i = ioprio2 }, NULL);
if (!ok) {
beep();
}
@@ -149,8 +151,40 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
+static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) {
+ if (LinuxProcess_isAutogroupEnabled() == false) {
+ beep();
+ return false;
+ }
+ bool anyTagged;
+ bool ok = MainPanel_foreachProcess(panel, LinuxProcess_changeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged);
+ if (!ok)
+ beep();
+ return anyTagged;
+}
+
+static Htop_Reaction Platform_actionHigherAutogroupPriority(State* st) {
+ if (Settings_isReadonly())
+ return HTOP_OK;
+
+ bool changed = Platform_changeAutogroupPriority(st->mainPanel, -1);
+ return changed ? HTOP_REFRESH : HTOP_OK;
+}
+
+static Htop_Reaction Platform_actionLowerAutogroupPriority(State* st) {
+ if (Settings_isReadonly())
+ return HTOP_OK;
+
+ bool changed = Platform_changeAutogroupPriority(st->mainPanel, 1);
+ return changed ? HTOP_REFRESH : HTOP_OK;
+}
+
void Platform_setBindings(Htop_Action* keys) {
keys['i'] = Platform_actionSetIOPriority;
+ keys['{'] = Platform_actionLowerAutogroupPriority;
+ keys['}'] = Platform_actionHigherAutogroupPriority;
+ keys[KEY_F(19)] = Platform_actionLowerAutogroupPriority; // Shift-F7
+ keys[KEY_F(20)] = Platform_actionHigherAutogroupPriority; // Shift-F8
}
const MeterClass* const Platform_meterTypes[] = {
@@ -162,6 +196,9 @@ const MeterClass* const Platform_meterTypes[] = {
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
+ &MemorySwapMeter_class,
+ &SysArchMeter_class,
+ &HugePageMeter_class,
&TasksMeter_class,
&UptimeMeter_class,
&BatteryMeter_class,
@@ -208,19 +245,25 @@ int Platform_getUptime() {
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
- int activeProcs, totalProcs, lastProc;
- *one = 0;
- *five = 0;
- *fifteen = 0;
-
FILE* fd = fopen(PROCDIR "/loadavg", "r");
- if (fd) {
- int total = fscanf(fd, "%32lf %32lf %32lf %32d/%32d %32d", one, five, fifteen,
- &activeProcs, &totalProcs, &lastProc);
- (void) total;
- assert(total == 6);
- fclose(fd);
- }
+ if (!fd)
+ goto err;
+
+ double scanOne, scanFive, scanFifteen;
+ int r = fscanf(fd, "%lf %lf %lf", &scanOne, &scanFive, &scanFifteen);
+ fclose(fd);
+ if (r != 3)
+ goto err;
+
+ *one = scanOne;
+ *five = scanFive;
+ *fifteen = scanFifteen;
+ return;
+
+err:
+ *one = NAN;
+ *five = NAN;
+ *fifteen = NAN;
}
int Platform_getMaxPid() {
@@ -235,12 +278,18 @@ int Platform_getMaxPid() {
return maxPid;
}
-double Platform_setCPUValues(Meter* this, int cpu) {
+double Platform_setCPUValues(Meter* this, unsigned int cpu) {
const LinuxProcessList* pl = (const LinuxProcessList*) this->pl;
- const CPUData* cpuData = &(pl->cpus[cpu]);
+ const CPUData* cpuData = &(pl->cpuData[cpu]);
double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod);
double percent;
double* v = this->values;
+
+ if (!cpuData->online) {
+ this->curItems = 0;
+ return NAN;
+ }
+
v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
if (this->pl->settings->detailedCPUTime) {
@@ -282,18 +331,16 @@ void Platform_setMemoryValues(Meter* this) {
const ProcessList* pl = this->pl;
const LinuxProcessList* lpl = (const LinuxProcessList*) pl;
- long int usedMem = pl->usedMem;
- long int buffersMem = pl->buffersMem;
- long int cachedMem = pl->cachedMem;
- usedMem -= buffersMem + cachedMem;
- this->total = pl->totalMem;
- this->values[0] = usedMem;
- this->values[1] = buffersMem;
- this->values[2] = cachedMem;
+ this->total = pl->totalMem > lpl->totalHugePageMem ? pl->totalMem - lpl->totalHugePageMem : pl->totalMem;
+ this->values[0] = pl->usedMem > lpl->totalHugePageMem ? pl->usedMem - lpl->totalHugePageMem : 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) {
this->values[0] -= lpl->zfs.size;
- this->values[2] += lpl->zfs.size;
+ this->values[3] += lpl->zfs.size;
}
}
@@ -301,6 +348,7 @@ void Platform_setSwapValues(Meter* this) {
const ProcessList* pl = this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
+ this->values[1] = pl->cachedSwap;
}
void Platform_setZramValues(Meter* this) {
@@ -366,8 +414,8 @@ char* Platform_getProcessEnv(pid_t pid) {
*/
char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
struct stat sb;
- struct dirent *de;
- DIR *dirp;
+ const struct dirent* de;
+ DIR* dirp;
ssize_t len;
int fd;
@@ -495,33 +543,27 @@ bool Platform_getDiskIO(DiskIOData* data) {
if (!fd)
return false;
- unsigned long int read_sum = 0, write_sum = 0, timeSpend_sum = 0;
+ char lastTopDisk[32] = { '\0' };
+
+ unsigned long long int read_sum = 0, write_sum = 0, timeSpend_sum = 0;
char lineBuffer[256];
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
char diskname[32];
- unsigned long int read_tmp, write_tmp, timeSpend_tmp;
- if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %lu %*u %*u %*u %lu %*u %*u %lu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) {
+ unsigned long long int read_tmp, write_tmp, timeSpend_tmp;
+ if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %llu %*u %*u %*u %llu %*u %*u %llu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) {
if (String_startsWith(diskname, "dm-"))
continue;
- /* only count root disks, e.g. do not count IO from sda and sda1 twice */
- if ((diskname[0] == 's' || diskname[0] == 'h')
- && diskname[1] == 'd'
- && isalpha((unsigned char)diskname[2])
- && isdigit((unsigned char)diskname[3]))
+ if (String_startsWith(diskname, "zram"))
continue;
- /* only count root disks, e.g. do not count IO from mmcblk0 and mmcblk0p1 twice */
- if (diskname[0] == 'm'
- && diskname[1] == 'm'
- && diskname[2] == 'c'
- && diskname[3] == 'b'
- && diskname[4] == 'l'
- && diskname[5] == 'k'
- && isdigit((unsigned char)diskname[6])
- && diskname[7] == 'p')
+ /* only count root disks, e.g. do not count IO from sda and sda1 twice */
+ if (lastTopDisk[0] && String_startsWith(diskname, lastTopDisk))
continue;
+ /* This assumes disks are listed directly before any of their partitions */
+ String_safeStrncpy(lastTopDisk, diskname, sizeof(lastTopDisk));
+
read_sum += read_tmp;
write_sum += write_tmp;
timeSpend_sum += timeSpend_tmp;
@@ -535,42 +577,35 @@ bool Platform_getDiskIO(DiskIOData* data) {
return true;
}
-bool Platform_getNetworkIO(unsigned long int* bytesReceived,
- unsigned long int* packetsReceived,
- unsigned long int* bytesTransmitted,
- unsigned long int* packetsTransmitted) {
+bool Platform_getNetworkIO(NetworkIOData* data) {
FILE* fd = fopen(PROCDIR "/net/dev", "r");
if (!fd)
return false;
- unsigned long int bytesReceivedSum = 0, packetsReceivedSum = 0, bytesTransmittedSum = 0, packetsTransmittedSum = 0;
+ memset(data, 0, sizeof(NetworkIOData));
char lineBuffer[512];
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
char interfaceName[32];
- unsigned long int bytesReceivedParsed, packetsReceivedParsed, bytesTransmittedParsed, packetsTransmittedParsed;
- if (sscanf(lineBuffer, "%31s %lu %lu %*u %*u %*u %*u %*u %*u %lu %lu",
+ unsigned long long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
+ if (sscanf(lineBuffer, "%31s %llu %llu %*u %*u %*u %*u %*u %*u %llu %llu",
interfaceName,
- &bytesReceivedParsed,
- &packetsReceivedParsed,
- &bytesTransmittedParsed,
- &packetsTransmittedParsed) != 5)
+ &bytesReceived,
+ &packetsReceived,
+ &bytesTransmitted,
+ &packetsTransmitted) != 5)
continue;
if (String_eq(interfaceName, "lo:"))
continue;
- bytesReceivedSum += bytesReceivedParsed;
- packetsReceivedSum += packetsReceivedParsed;
- bytesTransmittedSum += bytesTransmittedParsed;
- packetsTransmittedSum += packetsTransmittedParsed;
+ data->bytesReceived += bytesReceived;
+ data->packetsReceived += packetsReceived;
+ data->bytesTransmitted += bytesTransmitted;
+ data->packetsTransmitted += packetsTransmitted;
}
fclose(fd);
- *bytesReceived = bytesReceivedSum;
- *packetsReceived = packetsReceivedSum;
- *bytesTransmitted = bytesTransmittedSum;
- *packetsTransmitted = packetsTransmittedSum;
return true;
}
@@ -596,11 +631,11 @@ static unsigned long int parseBatInfo(const char* fileName, const unsigned short
memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
while (nBatteries < MAX_BATTERIES) {
- struct dirent* dirEntry = readdir(batteryDir);
+ const struct dirent* dirEntry = readdir(batteryDir);
if (!dirEntry)
break;
- char* entryName = dirEntry->d_name;
+ const char* entryName = dirEntry->d_name;
if (!String_startsWith(entryName, "BAT"))
continue;
@@ -653,7 +688,7 @@ static ACPresence procAcpiCheck(void) {
return AC_ERROR;
for (;;) {
- struct dirent* dirEntry = readdir(dir);
+ const struct dirent* dirEntry = readdir(dir);
if (!dirEntry)
break;
@@ -688,8 +723,7 @@ static ACPresence procAcpiCheck(void) {
break;
}
- if (dir)
- closedir(dir);
+ closedir(dir);
return isOn;
}
@@ -728,7 +762,7 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
unsigned long int totalRemain = 0;
for (;;) {
- struct dirent* dirEntry = readdir(dir);
+ const struct dirent* dirEntry = readdir(dir);
if (!dirEntry)
break;
@@ -753,7 +787,7 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
}
char* buf = buffer;
- char* line = NULL;
+ const char* line;
bool full = false;
bool now = false;
int fullSize = 0;
@@ -852,3 +886,160 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) {
Platform_Battery_cacheIsOnAC = *isOnAC;
Platform_Battery_cacheTime = now;
}
+
+void Platform_longOptionsUsage(const char* name)
+{
+#ifdef HAVE_LIBCAP
+ printf(
+" --drop-capabilities[=off|basic|strict] Drop Linux capabilities when running as root\n"
+" off - do not drop any capabilities\n"
+" basic (default) - drop all capabilities not needed by %s\n"
+" strict - drop all capabilities except those needed for\n"
+" core functionality\n", name);
+#else
+ (void) name;
+#endif
+}
+
+bool Platform_getLongOption(int opt, int argc, char** argv) {
+#ifndef HAVE_LIBCAP
+ (void) argc;
+ (void) argv;
+#endif
+
+ switch (opt) {
+#ifdef HAVE_LIBCAP
+ case 160: {
+ const char* mode = optarg;
+ if (!mode && optind < argc && argv[optind] != NULL &&
+ (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
+ mode = argv[optind++];
+ }
+
+ if (!mode || String_eq(mode, "basic")) {
+ Platform_capabilitiesMode = CAP_MODE_BASIC;
+ } else if (String_eq(mode, "off")) {
+ Platform_capabilitiesMode = CAP_MODE_OFF;
+ } else if (String_eq(mode, "strict")) {
+ Platform_capabilitiesMode = CAP_MODE_STRICT;
+ } else {
+ fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
+ exit(1);
+ }
+ return true;
+ }
+#endif
+
+ default:
+ break;
+ }
+ return false;
+}
+
+#ifdef HAVE_LIBCAP
+static int dropCapabilities(enum CapMode mode) {
+
+ if (mode == CAP_MODE_OFF)
+ return 0;
+
+ /* capabilities we keep to operate */
+ const cap_value_t keepcapsStrict[] = {
+ CAP_DAC_READ_SEARCH,
+ CAP_SYS_PTRACE,
+ };
+ const cap_value_t keepcapsBasic[] = {
+ CAP_DAC_READ_SEARCH, /* read non world-readable process files of other users, like /proc/[pid]/io */
+ CAP_KILL, /* send signals to processes of other users */
+ CAP_SYS_NICE, /* lower process nice value / change nice value for arbitrary processes */
+ CAP_SYS_PTRACE, /* read /proc/[pid]/exe */
+#ifdef HAVE_DELAYACCT
+ CAP_NET_ADMIN, /* communicate over netlink socket for delay accounting */
+#endif
+ };
+ const cap_value_t* const keepcaps = (mode == CAP_MODE_BASIC) ? keepcapsBasic : keepcapsStrict;
+ const size_t ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
+
+ cap_t caps = cap_init();
+ if (caps == NULL) {
+ fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (cap_clear(caps) < 0) {
+ fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
+ cap_free(caps);
+ return -1;
+ }
+
+ cap_t currCaps = cap_get_proc();
+ if (currCaps == NULL) {
+ fprintf(stderr, "Error: can not get current process capabilities: %s\n", strerror(errno));
+ cap_free(caps);
+ return -1;
+ }
+
+ for (size_t i = 0; i < ncap; i++) {
+ if (!CAP_IS_SUPPORTED(keepcaps[i]))
+ continue;
+
+ cap_flag_value_t current;
+ if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, &current) < 0) {
+ fprintf(stderr, "Error: can not get current value of capability %d: %s\n", keepcaps[i], strerror(errno));
+ cap_free(currCaps);
+ cap_free(caps);
+ return -1;
+ }
+
+ if (current != CAP_SET)
+ continue;
+
+ if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0) {
+ fprintf(stderr, "Error: can not set permitted capability %d: %s\n", keepcaps[i], strerror(errno));
+ cap_free(currCaps);
+ cap_free(caps);
+ return -1;
+ }
+
+ if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0) {
+ fprintf(stderr, "Error: can not set effective capability %d: %s\n", keepcaps[i], strerror(errno));
+ cap_free(currCaps);
+ cap_free(caps);
+ return -1;
+ }
+ }
+
+ if (cap_set_proc(caps) < 0) {
+ fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
+ cap_free(currCaps);
+ cap_free(caps);
+ return -1;
+ }
+
+ cap_free(currCaps);
+ cap_free(caps);
+
+ return 0;
+}
+#endif
+
+void Platform_init(void) {
+#ifdef HAVE_LIBCAP
+ if (dropCapabilities(Platform_capabilitiesMode) < 0)
+ exit(1);
+#endif
+
+ if (access(PROCDIR, R_OK) != 0) {
+ fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
+ exit(1);
+ }
+
+#ifdef HAVE_SENSORS_SENSORS_H
+ LibSensors_init();
+#endif
+}
+
+void Platform_done(void) {
+#ifdef HAVE_SENSORS_SENSORS_H
+ LibSensors_cleanup();
+#endif
+}
diff --git a/linux/Platform.h b/linux/Platform.h
index fe81448..0d6d4a9 100644
--- a/linux/Platform.h
+++ b/linux/Platform.h
@@ -7,16 +7,35 @@ 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>
+#include <stdint.h>
+#include <sys/time.h>
#include <sys/types.h>
#include "Action.h"
#include "BatteryMeter.h"
#include "DiskIOMeter.h"
+#include "Hashtable.h"
+#include "Macros.h"
#include "Meter.h"
+#include "NetworkIOMeter.h"
#include "Process.h"
#include "ProcessLocksScreen.h"
+#include "RichString.h"
#include "SignalsPanel.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
+#endif
+
extern const ProcessField Platform_defaultFields[];
@@ -38,7 +57,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid(void);
-double Platform_setCPUValues(Meter* this, int cpu);
+double Platform_setCPUValues(Meter* this, unsigned int cpu);
void Platform_setMemoryValues(Meter* this);
@@ -56,15 +75,57 @@ 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_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred);
bool Platform_getDiskIO(DiskIOData* data);
-bool Platform_getNetworkIO(unsigned long int* bytesReceived,
- unsigned long int* packetsReceived,
- unsigned long int* bytesTransmitted,
- unsigned long int* packetsTransmitted);
+bool Platform_getNetworkIO(NetworkIOData* data);
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC);
+
+static inline void Platform_getHostname(char* buffer, size_t size) {
+ Generic_hostname(buffer, size);
+}
+
+static inline void Platform_getRelease(char** string) {
+ *string = Generic_uname();
+}
+
+#ifdef HAVE_LIBCAP
+ #define PLATFORM_LONG_OPTIONS \
+ {"drop-capabilities", optional_argument, 0, 160},
+#else
+ #define PLATFORM_LONG_OPTIONS
+#endif
+
+void Platform_longOptionsUsage(const char* name);
+
+bool Platform_getLongOption(int opt, int argc, char** argv);
+
+static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
+ Generic_gettime_realtime(tv, msec);
+}
+
+static inline void Platform_gettime_monotonic(uint64_t* msec) {
+ Generic_gettime_monotonic(msec);
+}
+
+static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
+
+static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
+
+static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
+
+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 void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
+
+static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
-void Platform_getBattery(double *percent, ACPresence *isOnAC);
+static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
#endif
diff --git a/linux/PressureStallMeter.c b/linux/PressureStallMeter.c
index 7486968..ff34f9b 100644
--- a/linux/PressureStallMeter.c
+++ b/linux/PressureStallMeter.c
@@ -6,7 +6,7 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "PressureStallMeter.h"
+#include "linux/PressureStallMeter.h"
#include <stdbool.h>
#include <string.h>
@@ -25,7 +25,7 @@ static const int PressureStallMeter_attributes[] = {
PRESSURE_STALL_THREEHUNDRED
};
-static void PressureStallMeter_updateValues(Meter* this, char* buffer, size_t len) {
+static void PressureStallMeter_updateValues(Meter* this) {
const char* file;
if (strstr(Meter_name(this), "CPU")) {
file = "cpu";
@@ -47,18 +47,20 @@ static void PressureStallMeter_updateValues(Meter* this, char* buffer, size_t le
/* only print bar for ten (not sixty and threehundred), cause the sum is meaningless */
this->curItems = 1;
- xSnprintf(buffer, len, "%s %s %5.2lf%% %5.2lf%% %5.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
+ xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s %s %5.2lf%% %5.2lf%% %5.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
}
static void PressureStallMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast;
char buffer[20];
- xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[0]);
- RichString_writeAscii(out, CRT_colors[PRESSURE_STALL_TEN], buffer);
- xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[1]);
- RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer);
- xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[2]);
- RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer);
+ int len;
+
+ len = xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[0]);
+ RichString_appendnAscii(out, CRT_colors[PRESSURE_STALL_TEN], buffer, len);
+ len = xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[1]);
+ RichString_appendnAscii(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer, len);
+ len = xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[2]);
+ RichString_appendnAscii(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer, len);
}
const MeterClass PressureStallCPUSomeMeter_class = {
diff --git a/linux/PressureStallMeter.h b/linux/PressureStallMeter.h
index 1a0ad58..7317b35 100644
--- a/linux/PressureStallMeter.h
+++ b/linux/PressureStallMeter.h
@@ -12,6 +12,7 @@ in the source distribution for its full text.
#include "Meter.h"
+
extern const MeterClass PressureStallCPUSomeMeter_class;
extern const MeterClass PressureStallIOSomeMeter_class;
diff --git a/linux/ProcessField.h b/linux/ProcessField.h
index 6e2eff3..69bdff0 100644
--- a/linux/ProcessField.h
+++ b/linux/ProcessField.h
@@ -19,7 +19,6 @@ in the source distribution for its full text.
M_TRS = 42, \
M_DRS = 43, \
M_LRS = 44, \
- M_DT = 45, \
CTID = 100, \
VPID = 101, \
VXID = 102, \
@@ -44,9 +43,8 @@ in the source distribution for its full text.
M_PSSWP = 121, \
CTXT = 122, \
SECATTR = 123, \
- PROC_COMM = 124, \
- PROC_EXE = 125, \
- CWD = 126, \
+ AUTOGROUP_ID = 127, \
+ AUTOGROUP_NICE = 128, \
// End of list
diff --git a/linux/SELinuxMeter.c b/linux/SELinuxMeter.c
index 892f1e8..e3b076d 100644
--- a/linux/SELinuxMeter.c
+++ b/linux/SELinuxMeter.c
@@ -5,18 +5,17 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "SELinuxMeter.h"
+#include "linux/SELinuxMeter.h"
#include "CRT.h"
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
-#include <linux/magic.h>
#include <sys/statfs.h>
#include <sys/statvfs.h>
-#include "Macros.h"
#include "Object.h"
#include "XUtils.h"
@@ -35,7 +34,7 @@ static bool hasSELinuxMount(void) {
return false;
}
- if ((uint32_t)sfbuf.f_type != (uint32_t)SELINUX_MAGIC) {
+ if ((uint32_t)sfbuf.f_type != /* SELINUX_MAGIC */ 0xf97cff8cU) {
return false;
}
@@ -70,11 +69,11 @@ static bool isSelinuxEnforcing(void) {
return !!enforce;
}
-static void SELinuxMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
+static void SELinuxMeter_updateValues(Meter* this) {
enabled = isSelinuxEnabled();
enforcing = isSelinuxEnforcing();
- xSnprintf(buffer, len, "%s%s", enabled ? "enabled" : "disabled", enabled ? (enforcing ? "; mode: enforcing" : "; mode: permissive") : "");
+ xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s%s", enabled ? "enabled" : "disabled", enabled ? (enforcing ? "; mode: enforcing" : "; mode: permissive") : "");
}
const MeterClass SELinuxMeter_class = {
diff --git a/linux/SELinuxMeter.h b/linux/SELinuxMeter.h
index d79ad01..453940c 100644
--- a/linux/SELinuxMeter.h
+++ b/linux/SELinuxMeter.h
@@ -9,6 +9,7 @@ in the source distribution for its full text.
#include "Meter.h"
+
extern const MeterClass SELinuxMeter_class;
#endif /* HEADER_SELinuxMeter */
diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c
index 61bb59b..245bb7b 100644
--- a/linux/SystemdMeter.c
+++ b/linux/SystemdMeter.c
@@ -5,7 +5,7 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "SystemdMeter.h"
+#include "linux/SystemdMeter.h"
#include <dlfcn.h>
#include <fcntl.h>
@@ -19,10 +19,22 @@ in the source distribution for its full text.
#include "Macros.h"
#include "Object.h"
#include "RichString.h"
+#include "Settings.h"
#include "XUtils.h"
+#if defined(BUILD_STATIC) && defined(HAVE_LIBSYSTEMD)
+#include <systemd/sd-bus.h>
+#endif
-#define INVALID_VALUE ((unsigned int)-1)
+
+#ifdef BUILD_STATIC
+
+#define sym_sd_bus_open_system sd_bus_open_system
+#define sym_sd_bus_get_property_string sd_bus_get_property_string
+#define sym_sd_bus_get_property_trivial sd_bus_get_property_trivial
+#define sym_sd_bus_unref sd_bus_unref
+
+#else
typedef void sd_bus;
typedef void sd_bus_error;
@@ -30,19 +42,35 @@ static int (*sym_sd_bus_open_system)(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*);
+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;
-static void* dlopenHandle = NULL;
-static sd_bus* bus = NULL;
static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
free(systemState);
systemState = NULL;
+#ifdef BUILD_STATIC
+# ifdef HAVE_LIBSYSTEMD
+ if (bus) {
+ sym_sd_bus_unref(bus);
+ }
+ bus = NULL;
+# endif /* HAVE_LIBSYSTEMD */
+#else /* BUILD_STATIC */
if (bus && dlopenHandle) {
sym_sd_bus_unref(bus);
}
@@ -52,9 +80,12 @@ static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
dlclose(dlopenHandle);
dlopenHandle = NULL;
}
+#endif /* BUILD_STATIC */
}
+#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
static int updateViaLib(void) {
+#ifndef BUILD_STATIC
if (!dlopenHandle) {
dlopenHandle = dlopen("libsystemd.so.0", RTLD_LAZY);
if (!dlopenHandle)
@@ -76,6 +107,7 @@ static int updateViaLib(void) {
#undef resolve
}
+#endif /* !BUILD_STATIC */
int r;
@@ -152,15 +184,21 @@ busfailure:
bus = NULL;
return -2;
+#ifndef BUILD_STATIC
dlfailure:
if (dlopenHandle) {
dlclose(dlopenHandle);
dlopenHandle = NULL;
}
return -1;
+#endif /* !BUILD_STATIC */
}
+#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
static void updateViaExec(void) {
+ if (Settings_isReadonly())
+ return;
+
int fdpair[2];
if (pipe(fdpair) < 0)
return;
@@ -181,15 +219,15 @@ static void updateViaExec(void) {
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
- execl("/bin/systemctl",
- "/bin/systemctl",
- "show",
- "--property=SystemState",
- "--property=NFailedUnits",
- "--property=NNames",
- "--property=NJobs",
- "--property=NInstalledJobs",
- NULL);
+ execlp("systemctl",
+ "systemctl",
+ "show",
+ "--property=SystemState",
+ "--property=NFailedUnits",
+ "--property=NNames",
+ "--property=NJobs",
+ "--property=NInstalledJobs",
+ NULL);
exit(127);
}
close(fdpair[1]);
@@ -213,8 +251,7 @@ static void updateViaExec(void) {
if (newline) {
*newline = '\0';
}
- free(systemState);
- systemState = xStrdup(lineBuffer + strlen("SystemState="));
+ free_and_xStrdup(&systemState, lineBuffer + strlen("SystemState="));
} else if (String_startsWith(lineBuffer, "NFailedUnits=")) {
nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10);
} else if (String_startsWith(lineBuffer, "NNames=")) {
@@ -229,15 +266,19 @@ static void updateViaExec(void) {
fclose(commandOutput);
}
-static void SystemdMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
+static void SystemdMeter_updateValues(Meter* this) {
free(systemState);
systemState = NULL;
nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE;
+#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
if (updateViaLib() < 0)
updateViaExec();
+#else
+ updateViaExec();
+#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
- xSnprintf(buffer, size, "%s", systemState ? systemState : "???");
+ xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", systemState ? systemState : "???");
}
static int zeroDigitColor(unsigned int value) {
@@ -265,8 +306,9 @@ static int valueDigitColor(unsigned int value) {
static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
char buffer[16];
+ int len;
- int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : 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");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " (");
@@ -274,40 +316,44 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
if (nFailedUnits == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
+ len = 1;
} else {
- xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits);
}
- RichString_appendAscii(out, zeroDigitColor(nFailedUnits), buffer);
+ RichString_appendnAscii(out, zeroDigitColor(nFailedUnits), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nNames == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
+ len = 1;
} else {
- xSnprintf(buffer, sizeof(buffer), "%u", nNames);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", nNames);
}
- RichString_appendAscii(out, valueDigitColor(nNames), buffer);
+ RichString_appendnAscii(out, valueDigitColor(nNames), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) (");
if (nJobs == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
+ len = 1;
} else {
- xSnprintf(buffer, sizeof(buffer), "%u", nJobs);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", nJobs);
}
- RichString_appendAscii(out, zeroDigitColor(nJobs), buffer);
+ RichString_appendnAscii(out, zeroDigitColor(nJobs), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nInstalledJobs == INVALID_VALUE) {
buffer[0] = '?';
buffer[1] = '\0';
+ len = 1;
} else {
- xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs);
+ len = xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs);
}
- RichString_appendAscii(out, valueDigitColor(nInstalledJobs), buffer);
+ RichString_appendnAscii(out, valueDigitColor(nInstalledJobs), buffer, len);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)");
}
diff --git a/linux/SystemdMeter.h b/linux/SystemdMeter.h
index 0f226d6..98ce6f7 100644
--- a/linux/SystemdMeter.h
+++ b/linux/SystemdMeter.h
@@ -10,6 +10,7 @@ in the source distribution for its full text.
#include "Meter.h"
+
extern const MeterClass SystemdMeter_class;
#endif /* HEADER_SystemdMeter */
diff --git a/linux/ZramMeter.c b/linux/ZramMeter.c
index 723de0a..e1e27b7 100644
--- a/linux/ZramMeter.c
+++ b/linux/ZramMeter.c
@@ -1,4 +1,6 @@
-#include "ZramMeter.h"
+#include "linux/ZramMeter.h"
+
+#include <stddef.h>
#include "CRT.h"
#include "Meter.h"
@@ -11,7 +13,9 @@ static const int ZramMeter_attributes[] = {
ZRAM
};
-static void ZramMeter_updateValues(Meter* this, char* buffer, size_t size) {
+static void ZramMeter_updateValues(Meter* this) {
+ char* buffer = this->txtBuffer;
+ size_t size = sizeof(this->txtBuffer);
int written;
Platform_setZramValues(this);
diff --git a/linux/ZramMeter.h b/linux/ZramMeter.h
index 7cf7861..ddba1ba 100644
--- a/linux/ZramMeter.h
+++ b/linux/ZramMeter.h
@@ -3,6 +3,7 @@
#include "Meter.h"
+
extern const MeterClass ZramMeter_class;
#endif
diff --git a/linux/ZramStats.h b/linux/ZramStats.h
index 2305cfd..67aadcc 100644
--- a/linux/ZramStats.h
+++ b/linux/ZramStats.h
@@ -2,9 +2,9 @@
#define HEADER_ZramStats
typedef struct ZramStats_ {
- unsigned long long int totalZram;
- unsigned long long int usedZramComp;
- unsigned long long int usedZramOrig;
+ memory_t totalZram;
+ memory_t usedZramComp;
+ memory_t usedZramOrig;
} ZramStats;
#endif

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