From b618c379e13e7a2bb688cd54a64d199a267c0438 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 31 Mar 2023 00:10:40 +0300 Subject: This patch is for "Hide userland process threads mode is off" case. Below is a small example of two threads program, whose main thread statistics is incorrect in htop. Despite main thread is permanently sleeping, its CPU load is 100% (must be 0%). CPU load of secondary thread is correct (100%). void *thread_func(void *data) { while(1) { } return NULL; } int main() { pthread_t thread; pthread_create(&thread, NULL, thread_func, NULL); pthread_join(thread, NULL); } The reason is in there is a difference in behavior of some files in /proc/pid and /proc/pid/task/pid directories. For example, /proc/pid/stat shows agregated user and sys times for all threads of a process. For the details please see do_task_stat() implementation and this function behavior difference in dependence of last argument: https://elixir.bootlin.com/linux/v6.2.8/source/fs/proc/array.c#L652 So, the problem occurs because of user and sys times of main thread are polluted by secondary thread statistics. This patch fixes the problem by reading correct stat from /proc/pid/task/pid/stat for main thread. Looking at other files from /proc/pid directory I found /proc/pid/io file with the same problem: https://elixir.bootlin.com/linux/v6.2.8/source/fs/proc/base.c#L3029 This problem is also fixed in this patch by reading correct data from /proc/pid/task/pid/io file for main thread. /proc/pid directory files are declared in tgid_base_stuff, while /proc/pid/task/tid directory files are declared in tid_base_stuff in kernel's fs/proc/base.c file: https://elixir.bootlin.com/linux/v6.2.8/source/fs/proc/base.c#L3238 I checked the difference between rest of declarations and it looks like there is no more problems of such type affecting htop. --- linux/LinuxProcessList.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'linux/LinuxProcessList.c') diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index f8d56b70..b94c24a1 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -14,6 +14,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -382,11 +383,15 @@ static inline ProcessState LinuxProcessList_getProcessState(char state) { } } -static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) { - LinuxProcess* lp = (LinuxProcess*) process; +static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, char* command, size_t commLen) { + Process* process = &lp->super; char buf[MAX_READ + 1]; - ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf)); + char path[22] = "stat"; + if (scanMainThread) { + xSnprintf(path, sizeof(path), "task/%"PRIi32"/stat", (int32_t)process->pid); + } + ssize_t r = xReadfileat(procFd, path, buf, sizeof(buf)); if (r < 0) return false; @@ -607,9 +612,14 @@ static bool LinuxProcessList_updateUser(ProcessList* processList, Process* proce return true; } -static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long realtimeMs) { +static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, bool scanMainThread, unsigned long long realtimeMs) { + Process *proc = &process->super; + char path[20] = "io"; char buffer[1024]; - ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer)); + if (scanMainThread) { + xSnprintf(path, sizeof(path), "task/%"PRIi32"/io", (int32_t)proc->pid); + } + ssize_t r = xReadfileat(procFd, path, buffer, sizeof(buffer)); if (r < 0) { process->io_rate_read_bps = NAN; process->io_rate_write_bps = NAN; @@ -1531,8 +1541,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ continue; } + bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !parent; if (ss->flags & PROCESS_FLAG_IO) - LinuxProcessList_readIoFile(lp, procFd, pl->realtimeMs); + LinuxProcessList_readIoFile(lp, procFd, scanMainThread, pl->realtimeMs); if (!LinuxProcessList_readStatmFile(lp, procFd)) goto errorReadingProcess; @@ -1580,7 +1591,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ char statCommand[MAX_NAME + 1]; unsigned long long int lasttimes = (lp->utime + lp->stime); unsigned long int tty_nr = proc->tty_nr; - if (!LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand))) + if (!LinuxProcessList_readStatFile(lp, procFd, scanMainThread, statCommand, sizeof(statCommand))) goto errorReadingProcess; if (lp->flags & PF_KTHREAD) { -- cgit v1.2.3