From 62c2d820add3dadea7569af051d2afd804f08432 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Thu, 4 Apr 2024 23:08:51 +0200 Subject: [PATCH 01/25] Make memory leak in xRealloc explicit There is an edge case in xRealloc upon failure to realloc memory that manifests when the memory area pointed to by ptr contains pointers. As the structure of ptr is opaque to xRealloc there's no chance for properly releasing the memory for those indirect pointers. The best we could do is release ptr itself, leaving behind an indirect leak of those pointers inside the memory pointed to by ptr. This memory leak is picked up by analyzers like deepcode and GCC 14's -fanalyzer, which causes a lengthy, hard-to-parse diagnostic. Leaving the memory allocated and failing on this code path is properly ignored though (at least with GCC 14). A better solution thus is to keep ptr untouched and instead leak the whole block which avoids this diagnostic on indirectly leaked memory which is free'd by bailing shortly after anyway. This is not a fix, neither was the code we had before. This commit mainly documents an edge case and tries to avoid some hard-to-understand (but unavoidable) leak by making it more blatantly obvious in the used tooling. --- XUtils.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/XUtils.c b/XUtils.c index 160386c98..bd4fe1411 100644 --- a/XUtils.c +++ b/XUtils.c @@ -63,9 +63,16 @@ void* xCalloc(size_t nmemb, size_t size) { void* xRealloc(void* ptr, size_t size) { assert(size > 0); - void* data = realloc(ptr, size); // deepcode ignore MemoryLeakOnRealloc: this goes to fail() + void* data = size ? realloc(ptr, size) : NULL; // deepcode ignore MemoryLeakOnRealloc: this goes to fail() if (!data) { - free(ptr); + /* free'ing ptr here causes an indirect memory leak if pointers + * are held as part of an potential array referenced in ptr. + * In GCC 14 -fanalyzer recognizes this leak, but fails to + * ignore it given that this path ends in a noreturn function. + * Thus to avoid this confusing diagnostic we opt to leave + * that pointer alone instead. + */ + // free(ptr); fail(); } return data; From 5518652e711a636781414c2a232c4dff1d498904 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Fri, 5 Apr 2024 13:07:21 +0200 Subject: [PATCH 02/25] Fix regression from storing monotonic time deltas When starting htop the timestamp of the first scan must be far in the past to avoid showing all processes as being started just recently. Fixes: b61685779cdf696ba4135a97ce66075c337c7562 --- Machine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Machine.c b/Machine.c index 6beb47b47..2c66571e7 100644 --- a/Machine.c +++ b/Machine.c @@ -101,11 +101,12 @@ void Machine_scanTables(Machine* this) { if (firstScanDone) { this->prevMonotonicMs = this->monotonicMs; + Platform_gettime_monotonic(&this->monotonicMs); } else { this->prevMonotonicMs = 0; + this->monotonicMs = 1; firstScanDone = true; } - Platform_gettime_monotonic(&this->monotonicMs); assert(this->monotonicMs > this->prevMonotonicMs); this->maxUserId = 0; From 6eed4898468c0fae5a6e2d0ab0bb6954723a356f Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Fri, 5 Apr 2024 19:13:59 +0200 Subject: [PATCH 03/25] Use size_t for array indices consistently This avoids a compiler warning with GCC 14 and LTO claiming uninitialized use of data[i] in the last loop. --- linux/LibSensors.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/linux/LibSensors.c b/linux/LibSensors.c index 537341612..bfc5903ba 100644 --- a/linux/LibSensors.c +++ b/linux/LibSensors.c @@ -230,7 +230,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns /* Only package temperature - copy to all cores */ if (coreTempCount == 0 && !isNaN(data[0])) { - for (unsigned int i = 1; i <= existingCPUs; i++) + for (size_t i = 1; i <= existingCPUs; i++) data[i] = data[0]; /* No further adjustments */ @@ -240,7 +240,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns /* No package temperature - set to max core temperature */ if (coreTempCount > 0 && isNaN(data[0])) { double maxTemp = -HUGE_VAL; - for (unsigned int i = 1; i <= existingCPUs; i++) { + for (size_t i = 1; i <= existingCPUs; i++) { if (isgreater(data[i], maxTemp)) { maxTemp = data[i]; data[0] = data[i]; @@ -252,7 +252,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns /* 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++) + for (size_t i = 2; i <= existingCPUs; i++) data[i] = data[1]; /* No further adjustments */ @@ -260,7 +260,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns } /* Half the temperatures, probably HT/SMT - copy to second half */ - const unsigned int delta = activeCPUs / 2; + const size_t delta = activeCPUs / 2; if (coreTempCount == delta) { memcpy(&data[delta + 1], &data[1], delta * sizeof(*data)); @@ -269,7 +269,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, uns } out: - for (unsigned int i = 0; i <= existingCPUs; i++) + for (size_t i = 0; i <= existingCPUs; i++) cpus[i].temperature = data[i]; free(data); From 5846b7df4e891e3db85e2719d5d28507e0da2468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 2 Apr 2024 19:30:40 +0200 Subject: [PATCH 04/25] Check for absolute paths in environment variables Only use the environment variables HOME and XDG_CONFIG_HOME, or the home directory from getpwuid(3) if they are absolute paths. Avoid different behavior depending on the current working directory. --- Settings.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Settings.c b/Settings.c index 6cbe46b9e..670acabe0 100644 --- a/Settings.c +++ b/Settings.c @@ -799,14 +799,14 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicMeters, H this->initialFilename = xStrdup(rcfile); } else { const char* home = getenv("HOME"); - if (!home) { + if (!home || home[0] != '/') { const struct passwd* pw = getpwuid(getuid()); - home = pw ? pw->pw_dir : ""; + home = (pw && pw->pw_dir && pw->pw_dir[0] == '/') ? pw->pw_dir : ""; } const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); char* configDir = NULL; char* htopDir = NULL; - if (xdgConfigHome) { + if (xdgConfigHome && xdgConfigHome[0] == '/') { this->initialFilename = String_cat(xdgConfigHome, "/htop/htoprc"); configDir = xStrdup(xdgConfigHome); htopDir = String_cat(xdgConfigHome, "/htop"); From 869220b5896e9bf5e9072254ab99779c960e3eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 2 Apr 2024 19:30:44 +0200 Subject: [PATCH 05/25] Use designated initializer more Designated initializers are supported in C99 and simplify struct initializations. All members not explicitly mentioned are default initialized to 0, and also modern compilers can warn on of those missing ones. --- Hashtable.c | 16 +++++++++------- Vector.c | 18 ++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Hashtable.c b/Hashtable.c index 2756b2328..041f25eb0 100644 --- a/Hashtable.c +++ b/Hashtable.c @@ -109,13 +109,15 @@ static size_t nextPrime(size_t n) { } Hashtable* Hashtable_new(size_t size, bool owner) { - Hashtable* this; - - this = xMalloc(sizeof(Hashtable)); - this->items = 0; - this->size = size ? nextPrime(size) : 13; - this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem)); - this->owner = owner; + size = size ? nextPrime(size) : 13; + + Hashtable* this = xMalloc(sizeof(Hashtable)); + *this = (Hashtable) { + .items = 0, + .size = size, + .buckets = xCalloc(size, sizeof(HashtableItem)), + .owner = owner, + }; assert(Hashtable_isConsistent(this)); return this; diff --git a/Vector.c b/Vector.c index 0e08c6508..aeb193917 100644 --- a/Vector.c +++ b/Vector.c @@ -25,14 +25,16 @@ Vector* Vector_new(const ObjectClass* type, bool owner, int size) { assert(size > 0); this = xMalloc(sizeof(Vector)); - this->growthRate = size; - this->array = (Object**) xCalloc(size, sizeof(Object*)); - this->arraySize = size; - this->items = 0; - this->type = type; - this->owner = owner; - this->dirty_index = -1; - this->dirty_count = 0; + *this = (Vector) { + .growthRate = size, + .array = xCalloc(size, sizeof(Object*)), + .arraySize = size, + .items = 0, + .type = type, + .owner = owner, + .dirty_index = -1, + .dirty_count = 0, + }; return this; } From 58c1cb0204281e2d16ed83464ad1bc37ae7b4de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Fri, 5 Apr 2024 19:12:29 +0200 Subject: [PATCH 06/25] Avoid multiplication in calloc argument --- Settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Settings.c b/Settings.c index 670acabe0..f9a58e374 100644 --- a/Settings.c +++ b/Settings.c @@ -790,7 +790,7 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicMeters, H this->topologyAffinity = false; #endif - this->screens = xCalloc(Platform_numberOfDefaultScreens * sizeof(ScreenSettings*), 1); + this->screens = xCalloc(Platform_numberOfDefaultScreens, sizeof(ScreenSettings*)); this->nScreens = 0; char* legacyDotfile = NULL; From 15b4bc45b2b0ccf2d93164b2b1b05fb94cc89220 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Wed, 3 Apr 2024 05:11:59 +0800 Subject: [PATCH 07/25] Read htoprc in modern location first before old one Read htop configuration file in the modern location first ("~/.config/htop/htoprc") before trying the legacy location ("~/.htoprc"). This would prevent a case where configuration files exist in both locations and the new configuration gets replaced by the old one. Signed-off-by: Kang-Che Sung --- Settings.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Settings.c b/Settings.c index f9a58e374..e2c94e2b6 100644 --- a/Settings.c +++ b/Settings.c @@ -838,8 +838,8 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicMeters, H #endif this->changed = false; this->delay = DEFAULT_DELAY; - bool ok = false; - if (legacyDotfile) { + bool ok = Settings_read(this, this->filename, initialCpuCount); + if (!ok && legacyDotfile) { ok = Settings_read(this, legacyDotfile, initialCpuCount); if (ok) { // Transition to new location and delete old configuration file @@ -849,9 +849,6 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicMeters, H } free(legacyDotfile); } - if (!ok) { - ok = Settings_read(this, this->filename, initialCpuCount); - } if (!ok) { this->screenTabs = true; this->changed = true; From a782ef357067962f60580478067f4023facab6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 30 Mar 2024 13:47:10 +0100 Subject: [PATCH 08/25] linux: move libnl code into separate file Move all the code using libnl functionality into a separate file to ease modifications. No functional change. --- Makefile.am | 5 ++ configure.ac | 1 + linux/LibNl.c | 123 ++++++++++++++++++++++++++++++++++++++ linux/LibNl.h | 18 ++++++ linux/LinuxProcessTable.c | 119 ++---------------------------------- 5 files changed, 153 insertions(+), 113 deletions(-) create mode 100644 linux/LibNl.c create mode 100644 linux/LibNl.h diff --git a/Makefile.am b/Makefile.am index 0d95e3701..ca2f90ba8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -214,6 +214,11 @@ linux_platform_sources = \ zfs/ZfsArcMeter.c \ zfs/ZfsCompressedArcMeter.c +if HAVE_DELAYACCT +linux_platform_headers += linux/LibNl.h +linux_platform_sources += linux/LibNl.c +endif + if HTOP_LINUX AM_LDFLAGS += -rdynamic myhtopplatheaders = $(linux_platform_headers) diff --git a/configure.ac b/configure.ac index 8c9e484aa..b3137dec0 100644 --- a/configure.ac +++ b/configure.ac @@ -692,6 +692,7 @@ case "$enable_delayacct" in AC_MSG_ERROR([bad value '$enable_delayacct' for --enable-delayacct]) ;; esac +AM_CONDITIONAL([HAVE_DELAYACCT], [test "$enable_delayacct" = yes]) AC_ARG_ENABLE([sensors], diff --git a/linux/LibNl.c b/linux/LibNl.c new file mode 100644 index 000000000..e61b14f6c --- /dev/null +++ b/linux/LibNl.c @@ -0,0 +1,123 @@ +/* +htop - linux/LibNl.c +(C) 2024 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#ifndef HAVE_DELAYACCT +#error Compiling this file requires HAVE_DELAYACCT +#endif + +#include "linux/LibNl.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +static void initNetlinkSocket(LinuxProcessTable* this) { + this->netlink_socket = nl_socket_alloc(); + if (this->netlink_socket == NULL) { + return; + } + if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { + return; + } + this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); +} + +void LibNl_destroyNetlinkSocket(LinuxProcessTable* this) { + if (!this->netlink_socket) + return; + + nl_close(this->netlink_socket); + nl_socket_free(this->netlink_socket); + this->netlink_socket = NULL; +} + +static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { + struct nlmsghdr* nlhdr; + struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1]; + const struct nlattr* nlattr; + struct taskstats stats; + int rem; + LinuxProcess* lp = (LinuxProcess*) linuxProcess; + + nlhdr = nlmsg_hdr(nlmsg); + + if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { + return NL_SKIP; + } + + if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { + memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats)); + assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid); + + // The xxx_delay_total values wrap around on overflow. + // (Linux Kernel "Documentation/accounting/taskstats-struct.rst") + unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time; + #define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0f, 100.0f) : NAN) + lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total); + lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total); + lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total); + #undef DELTAPERC + + lp->swapin_delay_total = stats.swapin_delay_total; + lp->blkio_delay_total = stats.blkio_delay_total; + lp->cpu_delay_total = stats.cpu_delay_total; + lp->delay_read_time = stats.ac_etime * 1000; + } + return NL_OK; +} + +void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) { + struct nl_msg* msg; + + if (!this->netlink_socket) { + 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) { + goto delayacct_failure; + } + + if (! (msg = nlmsg_alloc())) { + goto delayacct_failure; + } + + if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { + nlmsg_free(msg); + } + + if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) { + nlmsg_free(msg); + } + + if (nl_send_sync(this->netlink_socket, msg) < 0) { + goto delayacct_failure; + } + + if (nl_recvmsgs_default(this->netlink_socket) < 0) { + goto delayacct_failure; + } + + return; + +delayacct_failure: + process->swapin_delay_percent = NAN; + process->blkio_delay_percent = NAN; + process->cpu_delay_percent = NAN; +} diff --git a/linux/LibNl.h b/linux/LibNl.h new file mode 100644 index 000000000..e89c36948 --- /dev/null +++ b/linux/LibNl.h @@ -0,0 +1,18 @@ +#ifndef HEADER_LibNl +#define HEADER_LibNl +/* +htop - linux/LibNl.h +(C) 2024 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "linux/LinuxProcess.h" +#include "linux/LinuxProcessTable.h" + + +void LibNl_destroyNetlinkSocket(LinuxProcessTable* this); + +void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process); + +#endif /* HEADER_LibNl */ diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 428199d2a..9a5ffdb42 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -23,18 +23,6 @@ in the source distribution for its full text. #include #include -#ifdef HAVE_DELAYACCT -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - #include "Compat.h" #include "Hashtable.h" #include "Machine.h" @@ -55,6 +43,10 @@ in the source distribution for its full text. #include "linux/LinuxProcess.h" #include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep +#ifdef HAVE_DELAYACCT +#include "linux/LibNl.h" +#endif + #if defined(MAJOR_IN_MKDEV) #include #elif defined(MAJOR_IN_SYSMACROS) @@ -187,21 +179,6 @@ static void LinuxProcessTable_initTtyDrivers(LinuxProcessTable* this) { this->ttyDrivers = ttyDrivers; } -#ifdef HAVE_DELAYACCT - -static void LinuxProcessTable_initNetlinkSocket(LinuxProcessTable* this) { - this->netlink_socket = nl_socket_alloc(); - if (this->netlink_socket == NULL) { - return; - } - if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { - return; - } - this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); -} - -#endif - ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) { LinuxProcessTable* this = xCalloc(1, sizeof(LinuxProcessTable)); Object_setClass(this, Class(ProcessTable)); @@ -227,10 +204,7 @@ void ProcessTable_delete(Object* cast) { free(this->ttyDrivers); } #ifdef HAVE_DELAYACCT - if (this->netlink_socket) { - nl_close(this->netlink_socket); - nl_socket_free(this->netlink_socket); - } + LibNl_destroyNetlinkSocket(this); #endif free(this); } @@ -1010,87 +984,6 @@ static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd free_and_xStrdup(&process->super.procCwd, pathBuffer); } -#ifdef HAVE_DELAYACCT - -static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { - struct nlmsghdr* nlhdr; - struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1]; - const struct nlattr* nlattr; - struct taskstats stats; - int rem; - LinuxProcess* lp = (LinuxProcess*) linuxProcess; - - nlhdr = nlmsg_hdr(nlmsg); - - if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { - return NL_SKIP; - } - - if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { - memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats)); - assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid); - - // The xxx_delay_total values wrap around on overflow. - // (Linux Kernel "Documentation/accounting/taskstats-struct.rst") - unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time; - #define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0f, 100.0f) : NAN) - lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total); - lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total); - lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total); - #undef DELTAPERC - - lp->swapin_delay_total = stats.swapin_delay_total; - lp->blkio_delay_total = stats.blkio_delay_total; - lp->cpu_delay_total = stats.cpu_delay_total; - lp->delay_read_time = stats.ac_etime * 1000; - } - return NL_OK; -} - -static void LinuxProcessTable_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) { - struct nl_msg* msg; - - if (!this->netlink_socket) { - LinuxProcessTable_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) { - goto delayacct_failure; - } - - if (! (msg = nlmsg_alloc())) { - goto delayacct_failure; - } - - if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { - nlmsg_free(msg); - } - - if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) { - nlmsg_free(msg); - } - - if (nl_send_sync(this->netlink_socket, msg) < 0) { - goto delayacct_failure; - } - - if (nl_recvmsgs_default(this->netlink_socket) < 0) { - goto delayacct_failure; - } - - return; - -delayacct_failure: - process->swapin_delay_percent = NAN; - process->blkio_delay_percent = NAN; - process->cpu_delay_percent = NAN; -} - -#endif - static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t procFd) { char filename[MAX_NAME + 1]; ssize_t amtRead; @@ -1593,7 +1486,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar #ifdef HAVE_DELAYACCT if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) { - LinuxProcessTable_readDelayAcctData(this, lp); + LibNl_readDelayAcctData(this, lp); } #endif From 24b1513296fd61722166625ad46be1c56a5efc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 30 Mar 2024 13:47:14 +0100 Subject: [PATCH 09/25] linux: use dlopen for libnl3 instead of dynamic linking Instead of the current behavior of dynamic linking against libnl3 and libnl-genl-3 when configured with --enable-delayacct, load the shared libraries on request, if any delay accounting related process field is active, via dlopen(3), similar to libsensors and libsystemd. Distribution, who currently build htop with --enable-delayacct, need to explicitly add libnl3 and libnl-genl-3 as runtime dependencies to continue supporting delay accounting out-of-the-box. --- README.md | 5 +- configure.ac | 38 ++++-------- linux/LibNl.c | 159 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 151 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 7ace7cd18..40887254a 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ List of additional build-time dependencies (based on feature flags): * `sensors` * `hwloc` * `libcap` (v2.21 or later) -* `libnl-3` +* `libnl-3` and `libnl-genl-3` Install these and other required packages for C development from your package manager. @@ -137,7 +137,7 @@ To install on the local system run `make install`. By default `make install` ins - default: *no* * `--enable-delayacct`: enable Linux delay accounting support - - dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3* + - dependencies: *libnl-3-dev*(build-time) and *libnl-genl-3-dev*(build-time), at runtime *libnl-3* and *libnl-genl-3* are loaded via `dlopen(3)` if available and requested - default: *check* @@ -153,6 +153,7 @@ To install on the local system run `make install`. By default `make install` ins * `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`. * `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`. * `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely. +* `libnl-3` and `libnl-genl-3`, if `htop` was configured with `--enable-delayacct` and delay accounting process fields are active. `htop` checks for the availability of the actual runtime libraries as `htop` runs. diff --git a/configure.ac b/configure.ac index b3137dec0..0a90ca48b 100644 --- a/configure.ac +++ b/configure.ac @@ -658,40 +658,26 @@ case "$enable_delayacct" in elif test "$enable_static" = yes; then enable_delayacct=no else - m4_ifdef([PKG_PROG_PKG_CONFIG], [ - enable_delayacct=yes - PKG_PROG_PKG_CONFIG() - PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [enable_delayacct=no]) - PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [enable_delayacct=no]) - if test "$enable_delayacct" = yes; then - AM_CFLAGS="$AM_CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" - LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" - AC_DEFINE([HAVE_DELAYACCT], [1], [Define if delay accounting support should be enabled.]) - fi - ], [ - enable_delayacct=no - AC_MSG_NOTICE([Linux delay accounting support can not be enabled, cause pkg-config is required for checking its availability]) - ]) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -I/usr/include/libnl3" + AC_CHECK_HEADERS([netlink/attr.h netlink/handlers.h netlink/msg.h], [enable_delayacct=yes], [enable_delayacct=no]) + CFLAGS="$old_CFLAGS" fi ;; yes) - m4_ifdef([PKG_PROG_PKG_CONFIG], [ - PKG_PROG_PKG_CONFIG() - PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [AC_MSG_ERROR([can not find required library libnl3])]) - PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [AC_MSG_ERROR([can not find required library libnl3genl])]) - AM_CFLAGS="$AM_CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" - LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" - AC_DEFINE([HAVE_DELAYACCT], [1], [Define if delay accounting support should be enabled.]) - ], [ - pkg_m4_absent=1 - m4_warning([configure is generated without pkg.m4. 'make dist' target will be disabled.]) - AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.]) - ]) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -I/usr/include/libnl3" + AC_CHECK_HEADERS([netlink/attr.h netlink/handlers.h netlink/msg.h], [], [AC_MSG_ERROR([can not find required header files netlink/attr.h, netlink/handlers.h, netlink/msg.h])]) + CFLAGS="$old_CFLAGS" ;; *) AC_MSG_ERROR([bad value '$enable_delayacct' for --enable-delayacct]) ;; esac +if test "$enable_delayacct" = yes; then + AC_DEFINE([HAVE_DELAYACCT], [1], [Define if delay accounting support should be enabled.]) + AM_CFLAGS="$AM_CFLAGS -I/usr/include/libnl3" +fi AM_CONDITIONAL([HAVE_DELAYACCT], [test "$enable_delayacct" = yes]) diff --git a/linux/LibNl.c b/linux/LibNl.c index e61b14f6c..72465f225 100644 --- a/linux/LibNl.c +++ b/linux/LibNl.c @@ -13,36 +13,149 @@ in the source distribution for its full text. #include "linux/LibNl.h" +#include + #include #include #include #include #include -#include -#include -#include -#include +static void* libnlHandle; +static void* libnlGenlHandle; + +static void (*sym_nl_close)(struct nl_sock*); +static int (*sym_nl_connect)(struct nl_sock*, int); +static int (*sym_nl_recvmsgs_default)(struct nl_sock*); +static int (*sym_nl_send_sync)(struct nl_sock*, struct nl_msg*); +static struct nl_sock* (*sym_nl_socket_alloc)(void); +static void (*sym_nl_socket_free)(struct nl_sock*); +static int (*sym_nl_socket_modify_cb)(struct nl_sock*, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void*); +static void* (*sym_nla_data)(const struct nlattr*); +static struct nlattr* (*sym_nla_next)(const struct nlattr*, int*); +static int (*sym_nla_put_u32)(struct nl_msg*, int, uint32_t); +static struct nl_msg* (*sym_nlmsg_alloc)(void); +static struct nlmsghdr* (*sym_nlmsg_hdr)(struct nl_msg*); +static void (*sym_nlmsg_free)(struct nl_msg*); + +static int (*sym_genl_ctrl_resolve)(struct nl_sock*, const char*); +static int (*sym_genlmsg_parse)(struct nlmsghdr*, int, struct nlattr**, int, const struct nla_policy*); +static void* (*sym_genlmsg_put)(struct nl_msg*, uint32_t, uint32_t, int, int, int, uint8_t, uint8_t); + + +static void unload_libnl(void) { + sym_nl_close = NULL; + sym_nl_connect = NULL; + sym_nl_recvmsgs_default = NULL; + sym_nl_send_sync = NULL; + sym_nl_socket_alloc = NULL; + sym_nl_socket_free = NULL; + sym_nl_socket_modify_cb = NULL; + sym_nla_data = NULL; + sym_nla_next = NULL; + sym_nla_put_u32 = NULL; + sym_nlmsg_alloc = NULL; + sym_nlmsg_free = NULL; + sym_nlmsg_hdr = NULL; + + sym_genl_ctrl_resolve = NULL; + sym_genlmsg_parse = NULL; + sym_genlmsg_put = NULL; + + if (libnlGenlHandle) { + dlclose(libnlGenlHandle); + libnlGenlHandle = NULL; + } + if (libnlHandle) { + dlclose(libnlHandle); + libnlHandle = NULL; + } +} + +static int load_libnl(void) { + if (libnlHandle && libnlGenlHandle) + return 0; + + libnlHandle = dlopen("libnl-3.so", RTLD_LAZY); + if (!libnlHandle) { + libnlHandle = dlopen("libnl-3.so.200", RTLD_LAZY); + if (!libnlHandle) { + goto dlfailure; + } + } + + libnlGenlHandle = dlopen("libnl-genl-3.so", RTLD_LAZY); + if (!libnlGenlHandle) { + libnlGenlHandle = dlopen("libnl-genl-3.so.200", RTLD_LAZY); + if (!libnlGenlHandle) { + goto dlfailure; + } + } + + /* Clear any errors */ + dlerror(); + + #define resolve(handle, symbolname) do { \ + *(void **)(&sym_##symbolname) = dlsym(handle, #symbolname); \ + if (!sym_##symbolname || dlerror() != NULL) { \ + goto dlfailure; \ + } \ + } while(0) + + resolve(libnlHandle, nl_close); + resolve(libnlHandle, nl_connect); + resolve(libnlHandle, nl_recvmsgs_default); + resolve(libnlHandle, nl_send_sync); + resolve(libnlHandle, nl_socket_alloc); + resolve(libnlHandle, nl_socket_free); + resolve(libnlHandle, nl_socket_modify_cb); + resolve(libnlHandle, nla_data); + resolve(libnlHandle, nla_next); + resolve(libnlHandle, nla_put_u32); + resolve(libnlHandle, nlmsg_alloc); + resolve(libnlHandle, nlmsg_free); + resolve(libnlHandle, nlmsg_hdr); + + resolve(libnlGenlHandle, genl_ctrl_resolve); + resolve(libnlGenlHandle, genlmsg_parse); + resolve(libnlGenlHandle, genlmsg_put); + + #undef resolve + + return 0; + +dlfailure: + unload_libnl(); + return -1; +} + static void initNetlinkSocket(LinuxProcessTable* this) { - this->netlink_socket = nl_socket_alloc(); + if (load_libnl() < 0) { + return; + } + + this->netlink_socket = sym_nl_socket_alloc(); if (this->netlink_socket == NULL) { return; } - if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { + if (sym_nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { return; } - this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); + this->netlink_family = sym_genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); } void LibNl_destroyNetlinkSocket(LinuxProcessTable* this) { - if (!this->netlink_socket) - return; + if (this->netlink_socket) { + assert(libnlHandle); + + sym_nl_close(this->netlink_socket); + sym_nl_socket_free(this->netlink_socket); + this->netlink_socket = NULL; + } - nl_close(this->netlink_socket); - nl_socket_free(this->netlink_socket); - this->netlink_socket = NULL; + unload_libnl(); } static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { @@ -53,14 +166,14 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { int rem; LinuxProcess* lp = (LinuxProcess*) linuxProcess; - nlhdr = nlmsg_hdr(nlmsg); + nlhdr = sym_nlmsg_hdr(nlmsg); - if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { + if (sym_genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { return NL_SKIP; } if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { - memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats)); + memcpy(&stats, sym_nla_data(sym_nla_next(sym_nla_data(nlattr), &rem)), sizeof(stats)); assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid); // The xxx_delay_total values wrap around on overflow. @@ -90,27 +203,27 @@ void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) { } } - if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) { + if (sym_nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) { goto delayacct_failure; } - if (! (msg = nlmsg_alloc())) { + if (! (msg = sym_nlmsg_alloc())) { goto delayacct_failure; } - if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { - nlmsg_free(msg); + if (! sym_genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { + sym_nlmsg_free(msg); } - if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) { - nlmsg_free(msg); + if (sym_nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) { + sym_nlmsg_free(msg); } - if (nl_send_sync(this->netlink_socket, msg) < 0) { + if (sym_nl_send_sync(this->netlink_socket, msg) < 0) { goto delayacct_failure; } - if (nl_recvmsgs_default(this->netlink_socket) < 0) { + if (sym_nl_recvmsgs_default(this->netlink_socket) < 0) { goto delayacct_failure; } From ae932b4fa980acb8c80946d098c79e2ab3611d3f Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Wed, 3 Apr 2024 00:16:04 +0800 Subject: [PATCH 10/25] New Row_printNanoseconds() function It prints the time in one of these formats: nanoseconds, "fraction of seconds", "minutes + seconds + milliseconds". If the total time is greater than 10 minutes, it uses Row_printTime() to print higher time units. Signed-off-by: Kang-Che Sung --- Row.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ Row.h | 3 +++ 2 files changed, 54 insertions(+) diff --git a/Row.c b/Row.c index a175c9e33..66a4aa076 100644 --- a/Row.c +++ b/Row.c @@ -403,6 +403,57 @@ void Row_printTime(RichString* str, unsigned long long totalHundredths, bool col } } +void Row_printNanoseconds(RichString* str, unsigned long long totalNanoseconds, bool coloring) { + if (totalNanoseconds == 0) { + int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS]; + + RichString_appendAscii(str, shadowColor, " 0ns "); + return; + } + + char buffer[10]; + int len; + int baseColor = CRT_colors[PROCESS]; + + if (totalNanoseconds < 1000000) { + len = xSnprintf(buffer, sizeof(buffer), "%6luns ", (unsigned long)totalNanoseconds); + RichString_appendnAscii(str, baseColor, buffer, len); + return; + } + + unsigned long long totalMicroseconds = totalNanoseconds / 1000; + if (totalMicroseconds < 1000000) { + len = xSnprintf(buffer, sizeof(buffer), ".%06lus ", (unsigned long)totalMicroseconds); + RichString_appendnAscii(str, baseColor, buffer, len); + } + + unsigned long long totalSeconds = totalMicroseconds / 1000000; + unsigned long microseconds = totalMicroseconds % 1000000; + if (totalSeconds < 60) { + int width = 5; + unsigned long fraction = microseconds; + if (totalSeconds >= 10) { + width--; + fraction /= 10; + } + len = xSnprintf(buffer, sizeof(buffer), "%u.%0*lus ", (unsigned int)totalSeconds, width, fraction); + RichString_appendnAscii(str, baseColor, buffer, len); + return; + } + + if (totalSeconds < 600) { + unsigned int minutes = totalSeconds / 60; + unsigned int seconds = totalSeconds % 60; + unsigned int milliseconds = microseconds / 1000; + len = xSnprintf(buffer, sizeof(buffer), "%u:%02u.%03u ", minutes, seconds, milliseconds); + RichString_appendnAscii(str, baseColor, buffer, len); + return; + } + + unsigned long long totalHundredths = totalMicroseconds / 1000 / 10; + Row_printTime(str, totalHundredths, coloring); +} + void Row_printRate(RichString* str, double rate, bool coloring) { char buffer[16]; diff --git a/Row.h b/Row.h index f67c61036..6d882909e 100644 --- a/Row.h +++ b/Row.h @@ -154,6 +154,9 @@ void Row_printCount(RichString* str, unsigned long long number, bool coloring); /* Takes time in hundredths of a seconds. Prints 9 columns. */ void Row_printTime(RichString* str, unsigned long long totalHundredths, bool coloring); +/* Takes time in nanoseconds. Prints 9 columns. */ +void Row_printNanoseconds(RichString* str, unsigned long long totalNanoseconds, bool coloring); + /* Takes rate in bare unit (base 1024) per second. Prints 12 columns. */ void Row_printRate(RichString* str, double rate, bool coloring); From eb27a94ba411793aa1a25c331b3e1bb328739583 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Thu, 28 Mar 2024 06:35:50 +0800 Subject: [PATCH 11/25] Adapt GPU_TIME field for the new nanoseconds format Signed-off-by: Kang-Che Sung --- linux/LinuxProcess.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 5d145fffe..5ba305ec4 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -109,7 +109,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { #ifdef SCHEDULER_SUPPORT [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, }, #endif - [GPU_TIME] = { .name = "GPU_TIME", .title = " GPU_TIME", .description = "Total GPU time in nano seconds", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, .autoWidth = true, .autoTitleRightAlign = true, }, + [GPU_TIME] = { .name = "GPU_TIME", .title = " GPU_TIME", .description = "Total GPU time", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, .autoWidth = true, .autoTitleRightAlign = true, }, [GPU_PERCENT] = { .name = "GPU_PERCENT", .title = "GPU% ", .description = "Percentage of the GPU time the process used in the last sampling", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, }; @@ -242,7 +242,7 @@ static void LinuxProcess_rowWriteField(const Row* super, RichString* str, Proces case CMINFLT: Row_printCount(str, lp->cminflt, coloring); return; case CMAJFLT: Row_printCount(str, lp->cmajflt, coloring); return; case GPU_PERCENT: Row_printPercentage(lp->gpu_percent, buffer, n, 5, &attr); break; - case GPU_TIME: Row_printTime(str, lp->gpu_time / 1000 / 1000 / 10 /* nano to centi seconds */, coloring); return; + case GPU_TIME: Row_printNanoseconds(str, lp->gpu_time, coloring); return; case M_DRS: Row_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return; case M_LRS: if (lp->m_lrs) { From b4d5b5cea98f557a856c89500dc169f511a2b817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Fri, 5 May 2023 20:23:15 +0200 Subject: [PATCH 12/25] Linux: gather permitted capabilities via capget(2) #1211 has shown reading /proc//status might have a significant performance impact. It was started to be read by default to gather the permitted capabilities of the process. Gather permitted capabilities via the syscall capget(2) instead. cap_get_proc(3) is not used to avoid linking with -lcap. --- Process.c | 2 +- Process.h | 9 ++++++++- linux/LinuxProcessTable.c | 23 +++++++++++++++-------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Process.c b/Process.c index c343e77df..527fc201e 100644 --- a/Process.c +++ b/Process.c @@ -736,7 +736,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) { } break; case USER: - if (this->elevated_priv) + if (this->elevated_priv == TRI_ON) attr = CRT_colors[PROCESS_PRIV]; else if (host->htopUserId != this->st_uid) attr = CRT_colors[PROCESS_SHADOW]; diff --git a/Process.h b/Process.h index 6a80bc51a..066c14025 100644 --- a/Process.h +++ b/Process.h @@ -24,6 +24,13 @@ in the source distribution for its full text. #define DEFAULT_HIGHLIGHT_SECS 5 +typedef enum Tristate_ { + TRI_INITIAL = 0, + TRI_OFF = -1, + TRI_ON = 1, +} Tristate; + + /* Core process states (shared by platforms) * NOTE: The enum has an ordering that is important! * See processStateChar in process.c for ProcessSate -> letter mapping */ @@ -106,7 +113,7 @@ typedef struct Process_ { * - from file capabilities * - inherited from the ambient set */ - bool elevated_priv; + Tristate elevated_priv; /* Process runtime (in hundredth of a second) */ unsigned long long int time; diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 9a5ffdb42..57f93227b 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -20,7 +20,9 @@ in the source distribution for its full text. #include #include #include +#include #include +#include // raw syscall, no libcap // IWYU pragma: keep // IWYU pragma: no_include #include #include "Compat.h" @@ -391,14 +393,6 @@ static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t proc if (pid_ns_count > 1) process->isRunningInContainer = true; - } else if (String_startsWith(buffer, "CapPrm:")) { - char* ptr = buffer + strlen("CapPrm:"); - while (*ptr == ' ' || *ptr == '\t') - ptr++; - - uint64_t cap_permitted = fast_strtoull_hex(&ptr, 16); - process->elevated_priv = cap_permitted != 0 && process->st_uid != 0; - } else if (String_startsWith(buffer, "voluntary_ctxt_switches:")) { unsigned long vctxt; int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt); @@ -1481,6 +1475,19 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } + /* Gather permitted capabilities (thread-specific data) for non-root process. */ + if (proc->st_uid != 0 && proc->elevated_priv != TRI_OFF) { + struct __user_cap_header_struct header = { .version = _LINUX_CAPABILITY_VERSION_3, .pid = Process_getPid(proc) }; + struct __user_cap_data_struct data; + + long res = syscall(SYS_capget, &header, &data); + if (res == 0) { + proc->elevated_priv = (data.permitted != 0) ? TRI_ON : TRI_OFF; + } else { + proc->elevated_priv = TRI_OFF; + } + } + if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) LinuxProcessTable_readCGroupFile(lp, procFd); From 48181bc237d2b3118b6fb06b82e427082a92e0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 17 Apr 2023 19:04:04 +0200 Subject: [PATCH 13/25] Linux: update gathering information regarding threads Document for each block gathering information about a task whether the information is shared for the whole process or specific to a single userland thread. Also avoid system calls for process-shared information and reuse the information from the main task. --- Scheduling.c | 3 + linux/LibNl.c | 3 + linux/LinuxProcess.c | 3 + linux/LinuxProcessTable.c | 186 ++++++++++++++++++++++++++++++-------- 4 files changed, 156 insertions(+), 39 deletions(-) diff --git a/Scheduling.c b/Scheduling.c index d5c4b8a66..406caf7df 100644 --- a/Scheduling.c +++ b/Scheduling.c @@ -156,6 +156,9 @@ const char* Scheduling_formatPolicy(int policy) { } } +/* + * Gather scheduling policy (thread-specific data) + */ void Scheduling_readProcessPolicy(Process* proc) { proc->scheduling_policy = sched_getscheduler(Process_getPid(proc)); } diff --git a/linux/LibNl.c b/linux/LibNl.c index 72465f225..a256ede2a 100644 --- a/linux/LibNl.c +++ b/linux/LibNl.c @@ -193,6 +193,9 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { return NL_OK; } +/* + * Gather delay-accounting information (thread-specific data) + */ void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) { struct nl_msg* msg; diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 5ba305ec4..b97a9ad73 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -154,6 +154,9 @@ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) { #define SYS_ioprio_set __NR_ioprio_set #endif +/* + * Gather I/O scheduling class and priority (thread-specific data) + */ IOPriority LinuxProcess_updateIOPriority(Process* p) { IOPriority ioprio = 0; // Other OSes masquerading as Linux (NetBSD?) don't have this syscall diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 57f93227b..032128c7d 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -231,6 +231,9 @@ static inline ProcessState LinuxProcessTable_getProcessState(char state) { } } +/* + * Read /proc//stat (thread-specific data) + */ static bool LinuxProcessTable_readStatFile(LinuxProcess* lp, openat_arg_t procFd, const LinuxMachine* lhost, bool scanMainThread, char* command, size_t commLen) { Process* process = &lp->super; @@ -359,6 +362,9 @@ static bool LinuxProcessTable_readStatFile(LinuxProcess* lp, openat_arg_t procFd return true; } +/* + * Read /proc//status (thread-specific data) + */ static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t procFd) { LinuxProcess* lp = (LinuxProcess*) process; @@ -434,7 +440,16 @@ static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t proc return true; } -static bool LinuxProcessTable_updateUser(const Machine* host, Process* process, openat_arg_t procFd) { +/* + * Gather user of task (process-shared data) + */ +static bool LinuxProcessTable_updateUser(const Machine* host, Process* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + if (mainTask) { + process->st_uid = mainTask->super.st_uid; + process->user = mainTask->super.user; + return true; + } + struct stat sstat; #ifdef HAVE_OPENAT int statok = fstat(procFd, &sstat); @@ -452,6 +467,9 @@ static bool LinuxProcessTable_updateUser(const Machine* host, Process* process, return true; } +/* + * Read /proc//io (thread-specific data) + */ static void LinuxProcessTable_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread) { Process* process = &lp->super; const Machine* host = process->super.host; @@ -539,6 +557,9 @@ static void LinuxProcessTable_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* *d += v->size; } +/* + * Read /proc//maps (process-shared data) + */ static void LinuxProcessTable_readMaps(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host, bool calcSize, bool checkDeletedLib) { Process* proc = (Process*)process; @@ -651,7 +672,16 @@ static void LinuxProcessTable_readMaps(LinuxProcess* process, openat_arg_t procF } } -static bool LinuxProcessTable_readStatmFile(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host) { +/* + * Read /proc//statm (process-shared data) + */ +static bool LinuxProcessTable_readStatmFile(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host, const LinuxProcess* mainTask) { + if (mainTask) { + process->super.m_virt = mainTask->super.m_virt; + process->super.m_resident = mainTask->super.m_resident; + return true; + } + char statmdata[128] = {0}; if (xReadfileat(procFd, "statm", statmdata, sizeof(statmdata)) < 1) { @@ -679,6 +709,9 @@ static bool LinuxProcessTable_readStatmFile(LinuxProcess* process, openat_arg_t return r == 7; } +/* + * Read /proc//smaps (process-shared data) + */ static bool LinuxProcessTable_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, bool haveSmapsRollup) { //http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719 //kernel will return data in chunks of size PAGE_SIZE or less. @@ -805,8 +838,11 @@ static void LinuxProcessTable_readOpenVZData(LinuxProcess* process, openat_arg_t } } -#endif +#endif /* HAVE_OPENVZ */ +/* + * Read /proc//cgroup (thread-specific data) + */ static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "cgroup", "r"); if (!file) { @@ -900,7 +936,15 @@ static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t } } -static void LinuxProcessTable_readOomData(LinuxProcess* process, openat_arg_t procFd) { +/* + * Read /proc//oom_score (process-shared data) + */ +static void LinuxProcessTable_readOomData(LinuxProcess* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + if (mainTask) { + process->oom = mainTask->oom; + return; + } + char buffer[PROC_LINE_LENGTH + 1] = {0}; ssize_t oomRead = xReadfileat(procFd, "oom_score", buffer, sizeof(buffer)); @@ -921,7 +965,15 @@ static void LinuxProcessTable_readOomData(LinuxProcess* process, openat_arg_t pr process->oom = oom; } -static void LinuxProcessTable_readAutogroup(LinuxProcess* process, openat_arg_t procFd) { +/* + * Read /proc//autogroup (process-shared data) + */ +static void LinuxProcessTable_readAutogroup(LinuxProcess* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + if (mainTask) { + process->autogroup_id = mainTask->autogroup_id; + return; + } + process->autogroup_id = -1; char autogroup[64]; // space for two numeric values and fixed length strings @@ -938,7 +990,21 @@ static void LinuxProcessTable_readAutogroup(LinuxProcess* process, openat_arg_t } } -static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_t procFd) { +/* + * Read /proc//attr/current (process-shared data) + */ +static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + if (mainTask) { + const char* mainSecAttr = mainTask->secattr; + if (mainSecAttr) { + free_and_xStrdup(&process->secattr, mainSecAttr); + } else { + free(process->secattr); + process->secattr = NULL; + } + return; + } + char buffer[PROC_LINE_LENGTH + 1] = {0}; ssize_t attrdata = xReadfileat(procFd, "attr/current", buffer, sizeof(buffer)); @@ -958,7 +1024,21 @@ static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_ free_and_xStrdup(&process->secattr, buffer); } -static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd) { +/* + * Read /proc//cwd (process-shared data) + */ +static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + if (mainTask) { + const char* mainCwd = mainTask->super.procCwd; + if (mainCwd) { + free_and_xStrdup(&process->super.procCwd, mainCwd); + } else { + free(process->super.procCwd); + process->super.procCwd = NULL; + } + return; + } + char pathBuffer[PATH_MAX + 1] = {0}; #if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT) @@ -978,15 +1058,22 @@ static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd free_and_xStrdup(&process->super.procCwd, pathBuffer); } -static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t procFd) { - char filename[MAX_NAME + 1]; - ssize_t amtRead; +/* + * Read /proc//exe (process-shared data) + */ +static void LinuxProcessList_readExe(Process* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + if (mainTask) { + Process_updateExe(process, mainTask->super.procExe); + process->procExeDeleted = mainTask->super.procExeDeleted; + return; + } + + char filename[PATH_MAX + 1]; - /* execve could change /proc/[pid]/exe, so procExe should be updated */ #if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT) - amtRead = readlinkat(procFd, "exe", filename, sizeof(filename) - 1); + ssize_t amtRead = readlinkat(procFd, "exe", filename, sizeof(filename) - 1); #else - amtRead = Compat_readlink(procFd, "exe", filename, sizeof(filename) - 1); + ssize_t amtRead = Compat_readlink(procFd, "exe", filename, sizeof(filename) - 1); #endif if (amtRead > 0) { filename[amtRead] = 0; @@ -1016,9 +1103,16 @@ static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t pro Process_updateExe(process, NULL); process->procExeDeleted = false; } +} + +/* + * Read /proc//cmdline (process-shared data) + */ +static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + LinuxProcessList_readExe(process, procFd, mainTask); char command[4096 + 1]; // max cmdline length on Linux - amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command)); + ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command)); if (amtRead <= 0) return false; @@ -1178,15 +1272,21 @@ static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t pro Process_updateCmdline(process, command, tokenStart, tokenEnd); - /* /proc/[pid]/comm could change, so should be updated */ - if ((amtRead = xReadfileat(procFd, "comm", command, sizeof(command))) > 0) { + return true; +} + +/* + * Read /proc//comm (thread-specific data) + */ +static void LinuxProcessList_readComm(Process* process, openat_arg_t procFd) { + char command[4096 + 1]; // max cmdline length on Linux + ssize_t amtRead = xReadfileat(procFd, "comm", command, sizeof(command)); + if (amtRead > 0) { command[amtRead - 1] = '\0'; Process_updateComm(process, command); } else { Process_updateComm(process, NULL); } - - return true; } static char* LinuxProcessTable_updateTtyDevice(TtyDriver* ttyDrivers, unsigned long int tty_nr) { @@ -1259,7 +1359,7 @@ static bool isOlderThan(const Process* proc, unsigned int seconds) { return realtime - proc->starttime_ctime > seconds; } -static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_arg_t parentFd, const LinuxMachine* lhost, const char* dirname, const Process* parent) { +static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_arg_t parentFd, const LinuxMachine* lhost, const char* dirname, const LinuxProcess* mainTask) { ProcessTable* pt = (ProcessTable*) this; const Machine* host = &lhost->super; const Settings* settings = host->settings; @@ -1317,7 +1417,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } // Skip task directory of main thread - if (parent && pid == Process_getPid(parent)) + if (mainTask && pid == Process_getPid(&mainTask->super)) continue; #ifdef HAVE_OPENAT @@ -1333,10 +1433,11 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar Process* proc = ProcessTable_getProcess(pt, pid, &preExisting, LinuxProcess_new); LinuxProcess* lp = (LinuxProcess*) proc; - Process_setThreadGroup(proc, parent ? Process_getPid(parent) : pid); + Process_setThreadGroup(proc, mainTask ? Process_getPid(&mainTask->super) : pid); proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc); + assert(proc->isUserlandThread == (mainTask != NULL)); - LinuxProcessTable_recurseProcTree(this, procFd, lhost, "task", proc); + LinuxProcessTable_recurseProcTree(this, procFd, lhost, "task", lp); /* * These conditions will not trigger on first occurrence, cause we need to @@ -1367,11 +1468,12 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar continue; } - bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !parent; + const bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !mainTask; + if (ss->flags & PROCESS_FLAG_IO) LinuxProcessTable_readIoFile(lp, procFd, scanMainThread); - if (!LinuxProcessTable_readStatmFile(lp, procFd, lhost)) + if (!LinuxProcessTable_readStatmFile(lp, procFd, lhost, mainTask)) goto errorReadingProcess; { @@ -1391,8 +1493,8 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } else { /* Copy from process structure in threads and reset if setting got disabled */ - proc->usesDeletedLib = (proc->isUserlandThread && parent) ? parent->usesDeletedLib : false; - lp->m_lrs = (proc->isUserlandThread && parent) ? ((const LinuxProcess*)parent)->m_lrs : 0; + proc->usesDeletedLib = (proc->isUserlandThread && mainTask) ? mainTask->super.usesDeletedLib : false; + lp->m_lrs = (proc->isUserlandThread && mainTask) ? mainTask->m_lrs : 0; } if (prev != proc->usesDeletedLib) @@ -1400,7 +1502,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { - if (!parent) { + if (!mainTask) { // Read smaps file of each process only every second pass to improve performance static int smaps_flag = 0; if ((pid & 1) == smaps_flag) { @@ -1410,7 +1512,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar smaps_flag = !smaps_flag; } } else { - lp->m_pss = ((const LinuxProcess*)parent)->m_pss; + lp->m_pss = ((const LinuxProcess*)mainTask)->m_pss; } } @@ -1442,7 +1544,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar proc->percent_mem = proc->m_resident / (double)(host->totalMem) * 100.0; Process_updateCPUFieldWidths(proc->percent_cpu); - if (!LinuxProcessTable_updateUser(host, proc, procFd)) + if (!LinuxProcessTable_updateUser(host, proc, procFd, mainTask)) goto errorReadingProcess; if (!LinuxProcessTable_readStatusFile(proc, procFd)) @@ -1458,8 +1560,11 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar if (proc->isKernelThread) { Process_updateCmdline(proc, NULL, 0, 0); - } else if (!LinuxProcessTable_readCmdlineFile(proc, procFd)) { - Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); + } else { + if (!LinuxProcessTable_readCmdlineFile(proc, procFd, mainTask)) { + Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); + } + LinuxProcessList_readComm(proc, procFd); } Process_fillStarttimeBuffer(proc); @@ -1469,8 +1574,11 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar if (settings->updateProcessNames && proc->state != ZOMBIE) { if (proc->isKernelThread) { Process_updateCmdline(proc, NULL, 0, 0); - } else if (!LinuxProcessTable_readCmdlineFile(proc, procFd)) { - Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); + } else { + if (!LinuxProcessTable_readCmdlineFile(proc, procFd, mainTask)) { + Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); + } + LinuxProcessList_readComm(proc, procFd); } } } @@ -1498,19 +1606,19 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar #endif if (ss->flags & PROCESS_FLAG_LINUX_OOM) { - LinuxProcessTable_readOomData(lp, procFd); + LinuxProcessTable_readOomData(lp, procFd, mainTask); } if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) { - LinuxProcessTable_readSecattrData(lp, procFd); + LinuxProcessTable_readSecattrData(lp, procFd, mainTask); } if (ss->flags & PROCESS_FLAG_CWD) { - LinuxProcessTable_readCwd(lp, procFd); + LinuxProcessTable_readCwd(lp, procFd, mainTask); } if ((ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) { - LinuxProcessTable_readAutogroup(lp, procFd); + LinuxProcessTable_readAutogroup(lp, procFd, mainTask); } #ifdef SCHEDULER_SUPPORT @@ -1520,8 +1628,8 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar #endif if (ss->flags & PROCESS_FLAG_LINUX_GPU || GPUMeter_active()) { - if (parent) { - lp->gpu_time = ((const LinuxProcess*)parent)->gpu_time; + if (mainTask) { + lp->gpu_time = ((const LinuxProcess*)mainTask)->gpu_time; } else { GPU_readProcessData(this, lp, procFd); } From 213411173ca92fe3efe96d0e38e89562da3f295d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 17 Apr 2023 19:04:09 +0200 Subject: [PATCH 14/25] Linux: reorder some calls in LinuxProcessList_recurseProcTree() Improve maintainability by reordering calls in LinuxProcessList_recurseProcTree() to group them by side-effect or interdependency. --- linux/LinuxProcessTable.c | 60 ++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 032128c7d..9139dbe56 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -1470,9 +1470,6 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar const bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !mainTask; - if (ss->flags & PROCESS_FLAG_IO) - LinuxProcessTable_readIoFile(lp, procFd, scanMainThread); - if (!LinuxProcessTable_readStatmFile(lp, procFd, lhost, mainTask)) goto errorReadingProcess; @@ -1501,21 +1498,6 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar proc->mergedCommand.lastUpdate = 0; } - if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { - if (!mainTask) { - // Read smaps file of each process only every second pass to improve performance - static int smaps_flag = 0; - if ((pid & 1) == smaps_flag) { - LinuxProcessTable_readSmapsFile(lp, procFd, this->haveSmapsRollup); - } - if (pid == 1) { - smaps_flag = !smaps_flag; - } - } else { - lp->m_pss = ((const LinuxProcess*)mainTask)->m_pss; - } - } - char statCommand[MAX_NAME + 1]; unsigned long long int lasttimes = (lp->utime + lp->stime); unsigned long int last_tty_nr = proc->tty_nr; @@ -1531,10 +1513,6 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar proc->tty_name = LinuxProcessTable_updateTtyDevice(this->ttyDrivers, proc->tty_nr); } - if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) { - LinuxProcess_updateIOPriority(proc); - } - proc->percent_cpu = NAN; /* lhost->period might be 0 after system sleep */ if (lhost->period > 0.0) { @@ -1583,6 +1561,11 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } + /* + * Section gathering non-critical information that is independent from + * each other. + */ + /* Gather permitted capabilities (thread-specific data) for non-root process. */ if (proc->st_uid != 0 && proc->elevated_priv != TRI_OFF) { struct __user_cap_header_struct header = { .version = _LINUX_CAPABILITY_VERSION_3, .pid = Process_getPid(proc) }; @@ -1599,6 +1582,27 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) LinuxProcessTable_readCGroupFile(lp, procFd); + if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { + if (!mainTask) { + // Read smaps file of each process only every second pass to improve performance + static int smaps_flag = 0; + if ((pid & 1) == smaps_flag) { + LinuxProcessTable_readSmapsFile(lp, procFd, this->haveSmapsRollup); + } + if (pid == 1) { + smaps_flag = !smaps_flag; + } + } else { + lp->m_pss = ((const LinuxProcess*)mainTask)->m_pss; + lp->m_swap = ((const LinuxProcess*)mainTask)->m_swap; + lp->m_psswp = ((const LinuxProcess*)mainTask)->m_psswp; + } + } + + if (ss->flags & PROCESS_FLAG_IO) { + LinuxProcessTable_readIoFile(lp, procFd, scanMainThread); + } + #ifdef HAVE_DELAYACCT if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) { LibNl_readDelayAcctData(this, lp); @@ -1609,6 +1613,10 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar LinuxProcessTable_readOomData(lp, procFd, mainTask); } + if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) { + LinuxProcess_updateIOPriority(proc); + } + if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) { LinuxProcessTable_readSecattrData(lp, procFd, mainTask); } @@ -1635,15 +1643,15 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } + /* + * Final section after all data has been gathered + */ + if (!proc->cmdline && statCommand[0] && (proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) { Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); } - /* - * Final section after all data has been gathered - */ - proc->super.updated = true; Compat_openatArgClose(procFd); From 76a13dbb4ea82c0a22c8d3a74476b9446aa91a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 9 Jan 2024 22:55:34 +0100 Subject: [PATCH 15/25] Linux: do not always read /proc//status #1211 showed reading /proc//status might have a significant performance impact. Do not read by default only if actually needed, since the permitted capabilities are no longer gather from it. Improves: 8ea144df ("Linux: Refactor /proc//status parsing") Improves: #1211 --- linux/LinuxProcessTable.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 9139dbe56..64a0fd93f 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -1525,8 +1525,15 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar if (!LinuxProcessTable_updateUser(host, proc, procFd, mainTask)) goto errorReadingProcess; - if (!LinuxProcessTable_readStatusFile(proc, procFd)) - goto errorReadingProcess; + if (ss->flags & PROCESS_FLAG_LINUX_CTXT + || hideRunningInContainer +#ifdef HAVE_VSERVER + || ss->flags & PROCESS_FLAG_LINUX_VSERVER +#endif + ) { + if (!LinuxProcessTable_readStatusFile(proc, procFd)) + goto errorReadingProcess; + } if (!preExisting) { From 22d25db4678b844842af516c0f2d117382f7c632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 9 Jan 2024 23:07:27 +0100 Subject: [PATCH 16/25] Linux: detect container process by different PID namespace Container engines like docker and podman rely on Linux namespaces. If available check if the target process is inside a different PID namespace than htop. This check will mark sandbox'ed applications, e.g. under bubblewrap, but not light-wight containers, like distrobox. --- Process.h | 2 +- linux/LinuxProcessTable.c | 58 ++++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/Process.h b/Process.h index 066c14025..848894ca6 100644 --- a/Process.h +++ b/Process.h @@ -94,7 +94,7 @@ typedef struct Process_ { bool isUserlandThread; /* This process is running inside a container */ - bool isRunningInContainer; + Tristate isRunningInContainer; /* Controlling terminal identifier of the process */ unsigned long int tty_nr; diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 64a0fd93f..7044314e4 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -60,6 +60,10 @@ in the source distribution for its full text. #define PF_KTHREAD 0x00200000 #endif +/* Inode number of the PID namespace of htop */ +static ino_t rootPidNs = (ino_t)-1; + + static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) { assert(String_eq(mode, "r")); /* only currently supported mode */ @@ -193,6 +197,17 @@ ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) { // Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+) this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0); + // Read PID namespace inode number + { + struct stat sb; + int r = stat(PROCDIR "/self/ns/pid", &sb); + if (r == 0) { + rootPidNs = sb.st_ino; + } else { + rootPidNs = (ino_t)-1; + } + } + return super; } @@ -369,6 +384,7 @@ static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t proc LinuxProcess* lp = (LinuxProcess*) process; unsigned long ctxt = 0; + process->isRunningInContainer = TRI_OFF; #ifdef HAVE_VSERVER lp->vxid = 0; #endif @@ -397,7 +413,7 @@ static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t proc } if (pid_ns_count > 1) - process->isRunningInContainer = true; + process->isRunningInContainer = TRI_ON; } else if (String_startsWith(buffer, "voluntary_ctxt_switches:")) { unsigned long vctxt; @@ -1461,7 +1477,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar Compat_openatArgClose(procFd); continue; } - if (preExisting && hideRunningInContainer && proc->isRunningInContainer) { + if (preExisting && hideRunningInContainer && proc->isRunningInContainer == TRI_ON) { proc->super.updated = true; proc->super.show = false; Compat_openatArgClose(procFd); @@ -1525,16 +1541,6 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar if (!LinuxProcessTable_updateUser(host, proc, procFd, mainTask)) goto errorReadingProcess; - if (ss->flags & PROCESS_FLAG_LINUX_CTXT - || hideRunningInContainer -#ifdef HAVE_VSERVER - || ss->flags & PROCESS_FLAG_LINUX_VSERVER -#endif - ) { - if (!LinuxProcessTable_readStatusFile(proc, procFd)) - goto errorReadingProcess; - } - if (!preExisting) { #ifdef HAVE_OPENVZ @@ -1568,6 +1574,32 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } + /* Check if the process in inside a different PID namespace. */ + if (proc->isRunningInContainer == TRI_INITIAL && rootPidNs != (ino_t)-1) { + struct stat sb; +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) + int res = fstatat(procFd, "ns/pid", &sb, 0); +#else + char path[4096]; + xSnprintf(path, sizeof(path), "%s/ns/pid", procFd); + int res = stat(path, &sb); +#endif + if (res == 0) { + proc->isRunningInContainer = (sb.st_ino != rootPidNs) ? TRI_ON : TRI_OFF; + } + } + + if (ss->flags & PROCESS_FLAG_LINUX_CTXT + || (hideRunningInContainer && proc->isRunningInContainer == TRI_INITIAL) +#ifdef HAVE_VSERVER + || ss->flags & PROCESS_FLAG_LINUX_VSERVER +#endif + ) { + proc->isRunningInContainer = TRI_OFF; + if (!LinuxProcessTable_readStatusFile(proc, procFd)) + goto errorReadingProcess; + } + /* * Section gathering non-critical information that is independent from * each other. @@ -1662,7 +1694,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar proc->super.updated = true; Compat_openatArgClose(procFd); - if (hideRunningInContainer && proc->isRunningInContainer) { + if (hideRunningInContainer && proc->isRunningInContainer == TRI_ON) { proc->super.show = false; continue; } From 85c3c3a88a000481254e01740ad396143ac2bca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 9 Jan 2024 23:10:03 +0100 Subject: [PATCH 17/25] Linux: add process column whether it is marked a container process Might be useful for some users and for debugging the hideRunningInContainer detection. --- linux/LinuxProcess.c | 16 ++++++++++++++++ linux/LinuxProcess.h | 1 + linux/LinuxProcessTable.c | 2 +- linux/ProcessField.h | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index b97a9ad73..61fb10e9b 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -106,6 +106,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, }, [AUTOGROUP_ID] = { .name = "AUTOGROUP_ID", .title = "AGRP", .description = "The autogroup identifier of the process", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, }, [AUTOGROUP_NICE] = { .name = "AUTOGROUP_NICE", .title = " ANI", .description = "Nice value (the higher the value, the more other processes take priority) associated with the process autogroup", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, }, + [ISCONTAINER] = { .name = "ISCONTAINER", .title = "CONT ", .description = "Whether the process is running inside a child container", .flags = PROCESS_FLAG_LINUX_CONTAINER, }, #ifdef SCHEDULER_SUPPORT [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, }, #endif @@ -336,6 +337,19 @@ static void LinuxProcess_rowWriteField(const Row* super, RichString* str, Proces xSnprintf(buffer, n, "N/A "); } break; + case ISCONTAINER: + switch (this->isRunningInContainer) { + case TRI_ON: + xSnprintf(buffer, n, "YES "); + break; + case TRI_OFF: + xSnprintf(buffer, n, "NO "); + break; + default: + attr = CRT_colors[PROCESS_SHADOW]; + xSnprintf(buffer, n, "N/A "); + } + break; default: Process_writeField(this, str, field); return; @@ -438,6 +452,8 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce } case GPU_TIME: return SPACESHIP_NUMBER(p1->gpu_time, p2->gpu_time); + case ISCONTAINER: + return SPACESHIP_NUMBER(v1->isRunningInContainer, v2->isRunningInContainer); default: return Process_compareByKey_Base(v1, v2, key); } diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 5a1e62725..fafd7d004 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -30,6 +30,7 @@ in the source distribution for its full text. #define PROCESS_FLAG_LINUX_DELAYACCT 0x00040000 #define PROCESS_FLAG_LINUX_AUTOGROUP 0x00080000 #define PROCESS_FLAG_LINUX_GPU 0x00100000 +#define PROCESS_FLAG_LINUX_CONTAINER 0x00200000 typedef struct LinuxProcess_ { Process super; diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 7044314e4..2f8e4a5e8 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -1590,7 +1590,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } if (ss->flags & PROCESS_FLAG_LINUX_CTXT - || (hideRunningInContainer && proc->isRunningInContainer == TRI_INITIAL) + || ((hideRunningInContainer || ss->flags & PROCESS_FLAG_LINUX_CONTAINER) && proc->isRunningInContainer == TRI_INITIAL) #ifdef HAVE_VSERVER || ss->flags & PROCESS_FLAG_LINUX_VSERVER #endif diff --git a/linux/ProcessField.h b/linux/ProcessField.h index 735423b97..47c4199fe 100644 --- a/linux/ProcessField.h +++ b/linux/ProcessField.h @@ -50,6 +50,7 @@ in the source distribution for its full text. M_PRIV = 131, \ GPU_TIME = 132, \ GPU_PERCENT = 133, \ + ISCONTAINER = 134, \ // End of list From a6306f090abcb1316a81444dc5514d236409349b Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Fri, 5 Apr 2024 23:17:17 +0200 Subject: [PATCH 18/25] Properly handle RichString_extendLen when an external buffer already exists --- RichString.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/RichString.c b/RichString.c index ea9048177..556674b0d 100644 --- a/RichString.c +++ b/RichString.c @@ -20,18 +20,26 @@ in the source distribution for its full text. #define charBytes(n) (sizeof(CharType) * (n)) static void RichString_extendLen(RichString* this, int len) { - if (this->chlen <= RICHSTRING_MAXLEN) { + if (this->chptr == this->chstr) { + // String is in internal buffer if (len > RICHSTRING_MAXLEN) { + // Copy from internal buffer to allocated string this->chptr = xMalloc(charBytes(len + 1)); memcpy(this->chptr, this->chstr, charBytes(this->chlen)); + } else { + // Still fits in internal buffer, do nothing + assert(this->chlen <= RICHSTRING_MAXLEN); } } else { - if (len <= RICHSTRING_MAXLEN) { + // String is managed externally + if (len > RICHSTRING_MAXLEN) { + // Just reallocate the buffer accordingly + this->chptr = xRealloc(this->chptr, charBytes(len + 1)); + } else { + // Move string into internal buffer and free resources memcpy(this->chstr, this->chptr, charBytes(len)); free(this->chptr); this->chptr = this->chstr; - } else { - this->chptr = xRealloc(this->chptr, charBytes(len + 1)); } } From 0b0d50c50ebf4ea120440d2eb7d91dda11628898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 8 Apr 2024 12:50:42 +0200 Subject: [PATCH 19/25] Row_printNanoseconds fixes Don't print twice for 0.9s. Don't truncate on 1.2s, leading to an abort. --- Row.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Row.c b/Row.c index 66a4aa076..383257d99 100644 --- a/Row.c +++ b/Row.c @@ -425,13 +425,14 @@ void Row_printNanoseconds(RichString* str, unsigned long long totalNanoseconds, if (totalMicroseconds < 1000000) { len = xSnprintf(buffer, sizeof(buffer), ".%06lus ", (unsigned long)totalMicroseconds); RichString_appendnAscii(str, baseColor, buffer, len); + return; } unsigned long long totalSeconds = totalMicroseconds / 1000000; unsigned long microseconds = totalMicroseconds % 1000000; if (totalSeconds < 60) { int width = 5; - unsigned long fraction = microseconds; + unsigned long fraction = microseconds / 10; if (totalSeconds >= 10) { width--; fraction /= 10; From 3477fbf2c97cf95b66cf84af0c151c88e900e0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 8 Apr 2024 12:52:00 +0200 Subject: [PATCH 20/25] Linux: fix title alignments of GPU columns Percentage column was always broken, time column needs to be adjusted after recent formatting changes. --- linux/LinuxProcess.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 61fb10e9b..f809cc60f 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -110,8 +110,8 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { #ifdef SCHEDULER_SUPPORT [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, }, #endif - [GPU_TIME] = { .name = "GPU_TIME", .title = " GPU_TIME", .description = "Total GPU time", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, .autoWidth = true, .autoTitleRightAlign = true, }, - [GPU_PERCENT] = { .name = "GPU_PERCENT", .title = "GPU% ", .description = "Percentage of the GPU time the process used in the last sampling", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, + [GPU_TIME] = { .name = "GPU_TIME", .title = "GPU_TIME ", .description = "Total GPU time", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, + [GPU_PERCENT] = { .name = "GPU_PERCENT", .title = " GPU% ", .description = "Percentage of the GPU time the process used in the last sampling", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, }; Process* LinuxProcess_new(const Machine* host) { From 626db518b350bb9b68b27f8fb90d440efddc2d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 6 Apr 2024 20:34:58 +0200 Subject: [PATCH 21/25] Drop casts to same type --- linux/LinuxProcessTable.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 2f8e4a5e8..43fe1fec1 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -1632,9 +1632,9 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar smaps_flag = !smaps_flag; } } else { - lp->m_pss = ((const LinuxProcess*)mainTask)->m_pss; - lp->m_swap = ((const LinuxProcess*)mainTask)->m_swap; - lp->m_psswp = ((const LinuxProcess*)mainTask)->m_psswp; + lp->m_pss = mainTask->m_pss; + lp->m_swap = mainTask->m_swap; + lp->m_psswp = mainTask->m_psswp; } } @@ -1676,7 +1676,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar if (ss->flags & PROCESS_FLAG_LINUX_GPU || GPUMeter_active()) { if (mainTask) { - lp->gpu_time = ((const LinuxProcess*)mainTask)->gpu_time; + lp->gpu_time = mainTask->gpu_time; } else { GPU_readProcessData(this, lp, procFd); } From 56d7622902c2eed2f9f3a86591881ad3cf96f73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 6 Apr 2024 20:30:22 +0200 Subject: [PATCH 22/25] Use uppercase floating point literals --- linux/GPU.c | 6 +++--- linux/LibNl.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/linux/GPU.c b/linux/GPU.c index 37d5d7c48..a4af92072 100644 --- a/linux/GPU.c +++ b/linux/GPU.c @@ -87,7 +87,7 @@ void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t /* check only if active in last check or last scan was more than 5s ago */ if (lp->gpu_activityMs != 0 && host->monotonicMs - lp->gpu_activityMs < 5000) { - lp->gpu_percent = 0.0f; + lp->gpu_percent = 0.0F; return; } lp->gpu_activityMs = host->monotonicMs; @@ -222,11 +222,11 @@ void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t gputimeDelta = saturatingSub(new_gpu_time, lp->gpu_time); monotonicTimeDelta = host->monotonicMs - host->prevMonotonicMs; - lp->gpu_percent = 100.0f * gputimeDelta / (1000 * 1000) / monotonicTimeDelta; + lp->gpu_percent = 100.0F * gputimeDelta / (1000 * 1000) / monotonicTimeDelta; lp->gpu_activityMs = 0; } else - lp->gpu_percent = 0.0f; + lp->gpu_percent = 0.0F; out: diff --git a/linux/LibNl.c b/linux/LibNl.c index a256ede2a..ba1359f20 100644 --- a/linux/LibNl.c +++ b/linux/LibNl.c @@ -179,7 +179,7 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { // The xxx_delay_total values wrap around on overflow. // (Linux Kernel "Documentation/accounting/taskstats-struct.rst") unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time; - #define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0f, 100.0f) : NAN) + #define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0F, 100.0F) : NAN) lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total); lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total); lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total); From 4cf8c98e0571e17e9234e056a41510b1bb00f155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 6 Apr 2024 20:39:39 +0200 Subject: [PATCH 23/25] Drop return at end of function returning void --- Row.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Row.c b/Row.c index 383257d99..d795787e6 100644 --- a/Row.c +++ b/Row.c @@ -292,7 +292,6 @@ void Row_printKBytes(RichString* str, unsigned long long number, bool coloring) color = CRT_colors[PROCESS_SHADOW]; RichString_appendAscii(str, color, " N/A "); - return; } void Row_printBytes(RichString* str, unsigned long long number, bool coloring) { From 327593e5cd5d48a6ff38d2877d7faaf53a6a8892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 6 Apr 2024 20:40:33 +0200 Subject: [PATCH 24/25] Align parameter names in declarations with definitions --- DynamicScreen.h | 2 +- Machine.h | 2 +- Process.h | 4 ++-- linux/GPU.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DynamicScreen.h b/DynamicScreen.h index fb08ebc96..00aa5e174 100644 --- a/DynamicScreen.h +++ b/DynamicScreen.h @@ -25,7 +25,7 @@ typedef struct DynamicScreen_ { Hashtable* DynamicScreens_new(void); -void DynamicScreens_delete(Hashtable* dynamics); +void DynamicScreens_delete(Hashtable* screens); void DynamicScreen_done(DynamicScreen* this); diff --git a/Machine.h b/Machine.h index f7f82afa5..247f73367 100644 --- a/Machine.h +++ b/Machine.h @@ -88,7 +88,7 @@ bool Machine_isCPUonline(const Machine* this, unsigned int id); void Machine_populateTablesFromSettings(Machine* this, Settings* settings, Table* processTable); -void Machine_setTablesPanel(Machine* host, Panel* panel); +void Machine_setTablesPanel(Machine* this, Panel* panel); void Machine_scan(Machine* this); diff --git a/Process.h b/Process.h index 848894ca6..8c7ae76ee 100644 --- a/Process.h +++ b/Process.h @@ -226,9 +226,9 @@ typedef struct ProcessFieldData_ { typedef int32_t ProcessField; /* see ReservedField list in RowField.h */ // Implemented in platform-specific code: -void Process_writeField(const Process* row, RichString* str, ProcessField field); +void Process_writeField(const Process* this, RichString* str, ProcessField field); int Process_compare(const void* v1, const void* v2); -int Process_compareByParent(const Row* r1, const Row* v2); +int Process_compareByParent(const Row* r1, const Row* r2); void Process_delete(Object* cast); extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; #define Process_pidDigits Row_pidDigits diff --git a/linux/GPU.h b/linux/GPU.h index b502d1471..15fb5c75c 100644 --- a/linux/GPU.h +++ b/linux/GPU.h @@ -12,6 +12,6 @@ in the source distribution for its full text. #include "linux/LinuxProcessTable.h" -void GPU_readProcessData(LinuxProcessTable* lpl, LinuxProcess* lp, openat_arg_t procFd); +void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t procFd); #endif /* HEADER_GPU */ From 1a12d5852602a7507d5911408561a754c446539c Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Mon, 8 Apr 2024 10:48:24 +0200 Subject: [PATCH 25/25] Work around GCC14 memleak diagnostic While both pointers are identical, GCC-14 with -fanalyzer complains about these return statements to leak memory. The leak is only reported with LTO though. --- darwin/DarwinProcess.c | 2 +- dragonflybsd/DragonFlyBSDProcess.c | 2 +- freebsd/FreeBSDProcess.c | 2 +- linux/LinuxProcess.c | 2 +- netbsd/NetBSDProcess.c | 2 +- openbsd/OpenBSDProcess.c | 2 +- pcp/PCPProcess.c | 2 +- solaris/SolarisProcess.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 29563a648..1da4221c1 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -62,7 +62,7 @@ Process* DarwinProcess_new(const Machine* host) { this->taskAccess = true; this->translated = false; - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) { diff --git a/dragonflybsd/DragonFlyBSDProcess.c b/dragonflybsd/DragonFlyBSDProcess.c index 915ecd094..002378a77 100644 --- a/dragonflybsd/DragonFlyBSDProcess.c +++ b/dragonflybsd/DragonFlyBSDProcess.c @@ -56,7 +56,7 @@ Process* DragonFlyBSDProcess_new(const Machine* host) { DragonFlyBSDProcess* this = xCalloc(1, sizeof(DragonFlyBSDProcess)); Object_setClass(this, Class(DragonFlyBSDProcess)); Process_init(&this->super, host); - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) { diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c index 901f9dad8..662943c91 100644 --- a/freebsd/FreeBSDProcess.c +++ b/freebsd/FreeBSDProcess.c @@ -62,7 +62,7 @@ Process* FreeBSDProcess_new(const Machine* machine) { FreeBSDProcess* this = xCalloc(1, sizeof(FreeBSDProcess)); Object_setClass(this, Class(FreeBSDProcess)); Process_init(&this->super, machine); - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) { diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index f809cc60f..741fe19dc 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -118,7 +118,7 @@ Process* LinuxProcess_new(const Machine* host) { LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess)); Object_setClass(this, Class(LinuxProcess)); Process_init(&this->super, host); - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) { diff --git a/netbsd/NetBSDProcess.c b/netbsd/NetBSDProcess.c index 3af5a8728..5462e721a 100644 --- a/netbsd/NetBSDProcess.c +++ b/netbsd/NetBSDProcess.c @@ -219,7 +219,7 @@ Process* NetBSDProcess_new(const Machine* host) { NetBSDProcess* this = xCalloc(1, sizeof(NetBSDProcess)); Object_setClass(this, Class(NetBSDProcess)); Process_init(&this->super, host); - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) { diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c index 4ac7aef18..7f50b0f8d 100644 --- a/openbsd/OpenBSDProcess.c +++ b/openbsd/OpenBSDProcess.c @@ -211,7 +211,7 @@ Process* OpenBSDProcess_new(const Machine* host) { OpenBSDProcess* this = xCalloc(1, sizeof(OpenBSDProcess)); Object_setClass(this, Class(OpenBSDProcess)); Process_init(&this->super, host); - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) { diff --git a/pcp/PCPProcess.c b/pcp/PCPProcess.c index 27630fc52..e8cc1aade 100644 --- a/pcp/PCPProcess.c +++ b/pcp/PCPProcess.c @@ -97,7 +97,7 @@ Process* PCPProcess_new(const Machine* host) { PCPProcess* this = xCalloc(1, sizeof(PCPProcess)); Object_setClass(this, Class(PCPProcess)); Process_init(&this->super, host); - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) { diff --git a/solaris/SolarisProcess.c b/solaris/SolarisProcess.c index 2dd03f0f3..29e981dff 100644 --- a/solaris/SolarisProcess.c +++ b/solaris/SolarisProcess.c @@ -64,7 +64,7 @@ Process* SolarisProcess_new(const Machine* host) { SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess)); Object_setClass(this, Class(SolarisProcess)); Process_init(&this->super, host); - return &this->super; + return (Process*)this; } void Process_delete(Object* cast) {