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: 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 d5c4b8a6..406caf7d 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 72465f22..a256ede2 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 5ba305ec..b97a9ad7 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 57f93227..032128c7 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); } -- cgit v1.2.3