From 937052b231259a47d881d539ad5748245ef55b99 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Fri, 3 Jun 2022 08:55:20 +0200 Subject: New upstream version 3.2.1 --- linux/LinuxProcess.c | 14 ++++---- linux/LinuxProcessList.c | 87 +++++++++++++++++++++++++++++++++++++----------- linux/Platform.c | 11 ++++-- linux/Platform.h | 2 ++ linux/SystemdMeter.c | 21 +++++++----- 5 files changed, 96 insertions(+), 39 deletions(-) (limited to 'linux') diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 299b167..92be326 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -57,7 +57,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, }, [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, - [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, + [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, @@ -86,9 +86,9 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, #ifdef HAVE_DELAYACCT - [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD%", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, - [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, - [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWPD%", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, + [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, + [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = " IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, + [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, #endif [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, }, [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, }, @@ -270,9 +270,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces break; } #ifdef HAVE_DELAYACCT - case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 4, &attr); break; - case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 4, &attr); break; - case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 4, &attr); break; + case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 5, &attr); break; + case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 5, &attr); break; + case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break; #endif case CTXT: if (lp->ctxt_diff > 1000) { diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 5e18f6d..45b045c 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -166,6 +166,28 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { #endif +static unsigned int scanAvailableCPUsFromCPUinfo(LinuxProcessList* this) { + FILE* file = fopen(PROCCPUINFOFILE, "r"); + if (file == NULL) + return this->super.existingCPUs; + + unsigned int availableCPUs = 0; + + while (!feof(file)) { + char buffer[PROC_LINE_LENGTH]; + + if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) + break; + + if (String_startsWith(buffer, "processor")) + availableCPUs++; + } + + fclose(file); + + return availableCPUs ? availableCPUs : 1; +} + static void LinuxProcessList_updateCPUcount(ProcessList* super) { /* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD @@ -240,6 +262,12 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { if (existing < 1) return; + if (Running_containerized) { + /* LXC munges /proc/cpuinfo but not the /sys/devices/system/cpu/ files, + * so limit the visible CPUs to what the guest has been configured to see: */ + currExisting = active = scanAvailableCPUsFromCPUinfo(this); + } + #ifdef HAVE_SENSORS_SENSORS_H /* When started with offline CPUs, libsensors does not monitor those, * even when they become online. */ @@ -248,7 +276,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { #endif super->activeCPUs = active; - assert(existing == currExisting); + assert(Running_containerized || (existing == currExisting)); super->existingCPUs = currExisting; } @@ -1323,7 +1351,8 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc if (process->procExeDeleted) filename[filenameLen - markerLen] = '\0'; - process->mergedCommand.exeChanged |= oldExeDeleted ^ process->procExeDeleted; + if (oldExeDeleted != process->procExeDeleted) + process->mergedCommand.lastUpdate = 0; } Process_updateExe(process, filename); @@ -1389,6 +1418,21 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo return out; } +static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int seconds) { + assert(pl->realtimeMs > 0); + + /* Starttime might not yet be parsed */ + if (proc->starttime_ctime <= 0) + return false; + + uint64_t realtime = pl->realtimeMs / 1000; + + if (realtime < (uint64_t)proc->starttime_ctime) + return false; + + return realtime - proc->starttime_ctime > seconds; +} + static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) { ProcessList* pl = (ProcessList*) this; const struct dirent* entry; @@ -1446,22 +1490,22 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ if (parent && pid == parent->pid) continue; - bool preExisting; - Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new); - LinuxProcess* lp = (LinuxProcess*) proc; - - proc->tgid = parent ? parent->pid : pid; - proc->isUserlandThread = proc->pid != proc->tgid; - #ifdef HAVE_OPENAT int procFd = openat(dirFd, entry->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if (procFd < 0) - goto errorReadingProcess; + continue; #else char procFd[4096]; xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name); #endif + bool preExisting; + Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new); + LinuxProcess* lp = (LinuxProcess*) proc; + + proc->tgid = parent ? parent->pid : pid; + proc->isUserlandThread = proc->pid != proc->tgid; + LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period); /* @@ -1497,7 +1541,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ bool prev = proc->usesDeletedLib; if (!proc->isKernelThread && !proc->isUserlandThread && - ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted))) { + ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(pl, proc, 10)))) { // Check if we really should recalculate the M_LRS value for this process uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime; @@ -1514,7 +1558,8 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ lp->m_lrs = (proc->isUserlandThread && parent) ? ((const LinuxProcess*)parent)->m_lrs : 0; } - proc->mergedCommand.exeChanged |= prev ^ proc->usesDeletedLib; + if (prev != proc->usesDeletedLib) + proc->mergedCommand.lastUpdate = 0; } if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { @@ -1653,8 +1698,13 @@ errorReadingProcess: #endif if (preExisting) { - ProcessList_remove(pl, proc); + /* + * The only real reason for coming here (apart from Linux violating the /proc API) + * would be the process going away with its /proc files disappearing (!HAVE_OPENAT). + * However, we want to keep in the process list for now for the "highlight dying" mode. + */ } else { + /* A really short-lived process that we don't have full info about */ Process_delete((Object*)proc); } } @@ -1874,6 +1924,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { switch (buffer[0]) { case 'c': + tryRead("c_min", &lpl->zfs.min); tryRead("c_max", &lpl->zfs.max); tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed); break; @@ -1908,6 +1959,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0); lpl->zfs.size /= 1024; + lpl->zfs.min /= 1024; lpl->zfs.max /= 1024; lpl->zfs.MFU /= 1024; lpl->zfs.MRU /= 1024; @@ -2101,16 +2153,11 @@ static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) { if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) break; - if ( - (sscanf(buffer, "processor : %d", &cpuid) == 1) || - (sscanf(buffer, "processor: %d", &cpuid) == 1) - ) { + if (sscanf(buffer, "processor : %d", &cpuid) == 1) { continue; } else if ( (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || - (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) || - (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) || - (sscanf(buffer, "clock: %lfMHz", &frequency) == 1) + (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) ) { if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) { continue; diff --git a/linux/Platform.c b/linux/Platform.c index 775f9ae..38b66e8 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -358,8 +358,13 @@ void Platform_setMemoryValues(Meter* this) { this->values[4] = pl->availableMem; if (lpl->zfs.enabled != 0 && !Running_containerized) { - this->values[0] -= lpl->zfs.size; - this->values[3] += lpl->zfs.size; + // ZFS does not shrink below the value of zfs_arc_min. + unsigned long long int shrinkableSize = 0; + if (lpl->zfs.size > lpl->zfs.min) + shrinkableSize = lpl->zfs.size - lpl->zfs.min; + this->values[0] -= shrinkableSize; + this->values[3] += shrinkableSize; + this->values[4] += shrinkableSize; } } @@ -1035,7 +1040,7 @@ bool Platform_init(void) { char lineBuffer[256]; while (fgets(lineBuffer, sizeof(lineBuffer), fd)) { // detect lxc or overlayfs and guess that this means we are running containerized - if (String_startsWith(lineBuffer, "lxcfs ") || String_startsWith(lineBuffer, "overlay ")) { + if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay ")) { Running_containerized = true; break; } diff --git a/linux/Platform.h b/linux/Platform.h index f2c314f..e6fa161 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -51,6 +51,8 @@ extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); void Platform_done(void); +extern bool Running_containerized; + void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c index 567cfc7..53ae2d2 100644 --- a/linux/SystemdMeter.c +++ b/linux/SystemdMeter.c @@ -219,15 +219,18 @@ static void updateViaExec(void) { exit(1); dup2(fdnull, STDERR_FILENO); close(fdnull); - execlp("systemctl", - "systemctl", - "show", - "--property=SystemState", - "--property=NFailedUnits", - "--property=NNames", - "--property=NJobs", - "--property=NInstalledJobs", - NULL); + // Use of NULL in variadic functions must have a pointer cast. + // The NULL constant is not required by standard to have a pointer type. + execlp( + "systemctl", + "systemctl", + "show", + "--property=SystemState", + "--property=NFailedUnits", + "--property=NNames", + "--property=NJobs", + "--property=NInstalledJobs", + (char *)NULL); exit(127); } close(fdpair[1]); -- cgit v1.2.3