diff options
author | Christian Göttsche <cgzones@googlemail.com> | 2021-06-12 18:17:28 +0200 |
---|---|---|
committer | Benny Baumann <BenBE@geshi.org> | 2021-07-18 07:44:02 +0200 |
commit | 41af31be7ffbd34518b27aad56a4f54af6a8adf3 (patch) | |
tree | 6e86813a332e93f7ee22b873d7522fa766b4af97 | |
parent | c9abd788b17ab28424961310b7e224498510780c (diff) |
Rework CPU counting
Currently htop does not support offline CPUs and hot-swapping, e.g. via
echo 0 > /sys/devices/system/cpu/cpu2/online
Split the current single cpuCount variable into activeCPUs and
existingCPUs.
Supersedes: #650
Related: #580
-rw-r--r-- | Action.c | 2 | ||||
-rw-r--r-- | Affinity.c | 4 | ||||
-rw-r--r-- | AffinityPanel.c | 6 | ||||
-rw-r--r-- | AvailableMetersPanel.c | 4 | ||||
-rw-r--r-- | CPUMeter.c | 21 | ||||
-rw-r--r-- | CommandLine.c | 2 | ||||
-rw-r--r-- | LoadAverageMeter.c | 12 | ||||
-rw-r--r-- | Process.c | 2 | ||||
-rw-r--r-- | ProcessList.c | 3 | ||||
-rw-r--r-- | ProcessList.h | 3 | ||||
-rw-r--r-- | TasksMeter.c | 2 | ||||
-rw-r--r-- | darwin/DarwinProcessList.c | 8 | ||||
-rw-r--r-- | darwin/Platform.c | 12 | ||||
-rw-r--r-- | dragonflybsd/DragonFlyBSDProcessList.c | 10 | ||||
-rw-r--r-- | dragonflybsd/Platform.c | 2 | ||||
-rw-r--r-- | freebsd/FreeBSDProcessList.c | 10 | ||||
-rw-r--r-- | freebsd/Platform.c | 2 | ||||
-rw-r--r-- | linux/LibSensors.c | 44 | ||||
-rw-r--r-- | linux/LibSensors.h | 5 | ||||
-rw-r--r-- | linux/LinuxProcessList.c | 170 | ||||
-rw-r--r-- | linux/LinuxProcessList.h | 5 | ||||
-rw-r--r-- | linux/Platform.c | 10 | ||||
-rw-r--r-- | openbsd/OpenBSDProcessList.c | 29 | ||||
-rw-r--r-- | openbsd/Platform.c | 18 | ||||
-rw-r--r-- | pcp/PCPProcessList.c | 19 | ||||
-rw-r--r-- | solaris/Platform.c | 2 | ||||
-rw-r--r-- | solaris/SolarisProcessList.c | 14 | ||||
-rw-r--r-- | unsupported/UnsupportedProcessList.c | 3 |
28 files changed, 276 insertions, 148 deletions
@@ -302,7 +302,7 @@ static Htop_Reaction actionSetAffinity(State* st) { if (Settings_isReadonly()) return HTOP_OK; - if (st->pl->cpuCount == 1) + if (st->pl->activeCPUs == 1) return HTOP_OK; #if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) @@ -59,7 +59,7 @@ Affinity* Affinity_get(const Process* proc, ProcessList* pl) { if (ok) { affinity = Affinity_new(pl); if (hwloc_bitmap_last(cpuset) == -1) { - for (unsigned int i = 0; i < pl->cpuCount; i++) { + for (unsigned int i = 0; i < pl->existingCPUs; i++) { Affinity_add(affinity, i); } } else { @@ -93,7 +93,7 @@ Affinity* Affinity_get(const Process* proc, ProcessList* pl) { return NULL; Affinity* affinity = Affinity_new(pl); - for (unsigned int i = 0; i < pl->cpuCount; i++) { + for (unsigned int i = 0; i < pl->existingCPUs; i++) { if (CPU_ISSET(i, &cpuset)) { Affinity_add(affinity, i); } diff --git a/AffinityPanel.c b/AffinityPanel.c index 25169994..56d8d89c 100644 --- a/AffinityPanel.c +++ b/AffinityPanel.c @@ -383,7 +383,8 @@ Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) Panel_setHeader(super, "Use CPUs:"); unsigned int curCpu = 0; - for (unsigned int i = 0; i < pl->cpuCount; i++) { + for (unsigned int i = 0; i < pl->existingCPUs; i++) { + /* TODO: skip offline CPUs */ char number[16]; xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i)); unsigned cpu_width = 4 + strlen(number); @@ -427,7 +428,8 @@ Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) { Affinity_add(affinity, i); hwloc_bitmap_foreach_end(); #else - for (unsigned int i = 0; i < this->pl->cpuCount; i++) { + for (unsigned int i = 0; i < this->pl->existingCPUs; i++) { + /* TODO: skip offline CPUs */ const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i); if (item->value) { Affinity_add(affinity, item->cpu); diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index c098b470..384c8864 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -94,9 +94,9 @@ const PanelClass AvailableMetersPanel_class = { // Handle (&CPUMeter_class) entries in the AvailableMetersPanel static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const ProcessList* pl) { - if (pl->cpuCount > 1) { + if (pl->existingCPUs > 1) { Panel_add(super, (Object*) ListItem_new("CPU average", 0)); - for (unsigned int i = 1; i <= pl->cpuCount; i++) { + for (unsigned int i = 1; i <= pl->existingCPUs; i++) { char buffer[50]; xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(pl->settings, i - 1)); Panel_add(super, (Object*) ListItem_new(buffer, i)); @@ -43,7 +43,7 @@ static void CPUMeter_init(Meter* this) { unsigned int cpu = this->param; if (cpu == 0) { Meter_setCaption(this, "Avg"); - } else if (this->pl->cpuCount > 1) { + } else if (this->pl->activeCPUs > 1) { char caption[10]; xSnprintf(caption, sizeof(caption), "%3u", Settings_cpuId(this->pl->settings, cpu - 1)); Meter_setCaption(this, caption); @@ -59,21 +59,24 @@ static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) { } static void CPUMeter_updateValues(Meter* this) { + memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); + unsigned int cpu = this->param; - if (cpu > this->pl->cpuCount) { + if (cpu > this->pl->existingCPUs) { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "absent"); - for (uint8_t i = 0; i < this->curItems; i++) - this->values[i] = 0; return; } - memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); + + double percent = Platform_setCPUValues(this, cpu); + if (isnan(percent)) { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "offline"); + return; + } char cpuUsageBuffer[8] = { 0 }; char cpuFrequencyBuffer[16] = { 0 }; char cpuTemperatureBuffer[16] = { 0 }; - double percent = Platform_setCPUValues(this, cpu); - if (this->pl->settings->showCPUUsage) { xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent); } @@ -112,7 +115,7 @@ static void CPUMeter_display(const Object* cast, RichString* out) { int len; const Meter* this = (const Meter*)cast; - if (this->param > this->pl->cpuCount) { + if (this->param > this->pl->existingCPUs) { RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent"); return; } @@ -206,7 +209,7 @@ static void AllCPUsMeter_updateValues(Meter* this) { } static void CPUMeterCommonInit(Meter* this, int ncol) { - unsigned int cpus = this->pl->cpuCount; + unsigned int cpus = this->pl->existingCPUs; CPUMeterData* data = this->meterData; if (!data) { data = this->meterData = xMalloc(sizeof(CPUMeterData)); diff --git a/CommandLine.c b/CommandLine.c index 66df3777..d932bb04 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -295,7 +295,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { Hashtable* dm = DynamicMeters_new(); ProcessList* pl = ProcessList_new(ut, dm, flags.pidMatchList, flags.userId); - Settings* settings = Settings_new(pl->cpuCount); + Settings* settings = Settings_new(pl->activeCPUs); pl->settings = settings; Header* header = Header_new(pl, settings, 2); diff --git a/LoadAverageMeter.c b/LoadAverageMeter.c index c91e6a2d..bb3b05ed 100644 --- a/LoadAverageMeter.c +++ b/LoadAverageMeter.c @@ -47,12 +47,12 @@ static void LoadAverageMeter_updateValues(Meter* this) { if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; this->total = 1.0; - } else if (this->values[0] < this->pl->cpuCount) { + } else if (this->values[0] < this->pl->activeCPUs) { this->curAttributes = Medium_attributes; - this->total = this->pl->cpuCount; + this->total = this->pl->activeCPUs; } else { this->curAttributes = High_attributes; - this->total = 2 * this->pl->cpuCount; + this->total = 2 * this->pl->activeCPUs; } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]); @@ -79,12 +79,12 @@ static void LoadMeter_updateValues(Meter* this) { if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; this->total = 1.0; - } else if (this->values[0] < this->pl->cpuCount) { + } else if (this->values[0] < this->pl->activeCPUs) { this->curAttributes = Medium_attributes; - this->total = this->pl->cpuCount; + this->total = this->pl->activeCPUs; } else { this->curAttributes = High_attributes; - this->total = 2 * this->pl->cpuCount; + this->total = 2 * this->pl->activeCPUs; } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f", this->values[0]); @@ -821,7 +821,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field case PERCENT_NORM_CPU: { float cpuPercentage = this->percent_cpu; if (field == PERCENT_NORM_CPU) { - cpuPercentage /= this->processList->cpuCount; + cpuPercentage /= this->processList->activeCPUs; } if (cpuPercentage > 999.9F) { xSnprintf(buffer, n, "%4u ", (unsigned int)cpuPercentage); diff --git a/ProcessList.c b/ProcessList.c index d3a773b3..8ab4d8f6 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -34,7 +34,8 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users this->userId = userId; // set later by platform-specific code - this->cpuCount = 0; + this->activeCPUs = 0; + this->existingCPUs = 0; this->monotonicMs = 0; // always maintain valid realtime timestamps diff --git a/ProcessList.h b/ProcessList.h index da4a50da..2b9a5070 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -83,7 +83,8 @@ typedef struct ProcessList_ { memory_t usedSwap; memory_t cachedSwap; - unsigned int cpuCount; + unsigned int activeCPUs; + unsigned int existingCPUs; } ProcessList; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); diff --git a/TasksMeter.c b/TasksMeter.c index 3840bf9b..9fb6c804 100644 --- a/TasksMeter.c +++ b/TasksMeter.c @@ -28,7 +28,7 @@ static void TasksMeter_updateValues(Meter* this) { this->values[0] = pl->kernelThreads; this->values[1] = pl->userlandThreads; this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads; - this->values[3] = MINIMUM(pl->runningTasks, pl->cpuCount); + this->values[3] = MINIMUM(pl->runningTasks, pl->activeCPUs); this->total = pl->totalTasks; xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%d/%d", (int) this->values[3], (int) this->total); diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 91d62c4a..e58c3496 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -134,7 +134,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H ProcessList_init(&this->super, Class(DarwinProcess), usersTable, dynamicMeters, pidMatchList, userId); /* Initialize the CPU information */ - this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load); + this->super.activeCPUs = ProcessList_allocateCPULoadInfo(&this->prev_load); + // TODO: support offline CPUs and hot swapping + this->super.existingCPUs = this->super.activeCPUs; ProcessList_getHostInfo(&this->host_info); ProcessList_allocateCPULoadInfo(&this->curr_load); @@ -184,13 +186,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { /* Get the time difference */ dpl->global_diff = 0; - for (unsigned int i = 0; i < dpl->super.cpuCount; ++i) { + for (unsigned int i = 0; i < dpl->super.existingCPUs; ++i) { for (size_t j = 0; j < CPU_STATE_MAX; ++j) { dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; } } - const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount; + const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.activeCPUs; /* Clear the thread counts */ super->kernelThreads = 0; diff --git a/darwin/Platform.c b/darwin/Platform.c index 3fcf314d..e110f72b 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -186,21 +186,21 @@ int Platform_getMaxPid() { static double Platform_setCPUAverageValues(Meter* mtr) { const ProcessList* dpl = mtr->pl; - unsigned int cpus = dpl->cpuCount; + unsigned int activeCPUs = dpl->activeCPUs; double sumNice = 0.0; double sumNormal = 0.0; double sumKernel = 0.0; double sumPercent = 0.0; - for (unsigned int i = 1; i <= cpus; i++) { + for (unsigned int i = 1; i <= dpl->existingCPUs; i++) { sumPercent += Platform_setCPUValues(mtr, i); sumNice += mtr->values[CPU_METER_NICE]; sumNormal += mtr->values[CPU_METER_NORMAL]; sumKernel += mtr->values[CPU_METER_KERNEL]; } - mtr->values[CPU_METER_NICE] = sumNice / cpus; - mtr->values[CPU_METER_NORMAL] = sumNormal / cpus; - mtr->values[CPU_METER_KERNEL] = sumKernel / cpus; - return sumPercent / cpus; + mtr->values[CPU_METER_NICE] = sumNice / activeCPUs; + mtr->values[CPU_METER_NORMAL] = sumNormal / activeCPUs; + mtr->values[CPU_METER_KERNEL] = sumKernel / activeCPUs; + return sumPercent / activeCPUs; } double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index 9bf6ffef..823bde59 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -95,13 +95,15 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_o, &len, NULL, 0); } - pl->cpuCount = MAXIMUM(cpus, 1); + pl->existingCPUs = MAXIMUM(cpus, 1); + // TODO: support offline CPUs and hot swapping + pl->activeCPUs = pl->existingCPUs; if (cpus == 1 ) { dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData)); } else { // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - dfpl->cpus = xRealloc(dfpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData)); + dfpl->cpus = xRealloc(dfpl->cpus, (pl->existingCPUs + 1) * sizeof(CPUData)); } len = sizeof(kernelFScale); @@ -140,8 +142,8 @@ void ProcessList_delete(ProcessList* this) { static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) { const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl; - unsigned int cpus = pl->cpuCount; // actual CPU count - unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) + unsigned int cpus = pl->existingCPUs; // actual CPU count + unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) int cp_times_offset; assert(cpus > 0); diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 39983f08..30fb0031 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -159,7 +159,7 @@ int Platform_getMaxPid() { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const DragonFlyBSDProcessList* fpl = (const DragonFlyBSDProcessList*) this->pl; - unsigned int cpus = this->pl->cpuCount; + unsigned int cpus = this->pl->activeCPUs; const CPUData* cpuData; if (cpus == 1) { diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 1478e7b7..8c3f17db 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -125,13 +125,15 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H sysctl(MIB_kern_cp_times, 2, fpl->cp_times_o, &len, NULL, 0); } - pl->cpuCount = MAXIMUM(cpus, 1); + pl->existingCPUs = MAXIMUM(cpus, 1); + // TODO: support offline CPUs and hot swapping + pl->activeCPUs = pl->existingCPUs; if (cpus == 1 ) { fpl->cpus = xRealloc(fpl->cpus, sizeof(CPUData)); } else { // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - fpl->cpus = xRealloc(fpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData)); + fpl->cpus = xRealloc(fpl->cpus, (pl->existingCPUs + 1) * sizeof(CPUData)); } @@ -169,8 +171,8 @@ void ProcessList_delete(ProcessList* this) { static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { const FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; - unsigned int cpus = pl->cpuCount; // actual CPU count - unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) + unsigned int cpus = pl->existingCPUs; // actual CPU count + unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) int cp_times_offset; assert(cpus > 0); diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 73383b28..e27ba9a6 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -181,7 +181,7 @@ int Platform_getMaxPid() { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl; - unsigned int cpus = this->pl->cpuCount; + unsigned int cpus = this->pl->activeCPUs; const CPUData* cpuData; if (cpus == 1) { diff --git a/linux/LibSensors.c b/linux/LibSensors.c index 5780c74f..5a92a351 100644 --- a/linux/LibSensors.c +++ b/linux/LibSensors.c @@ -32,10 +32,10 @@ static void* dlopenHandle = NULL; #endif /* BUILD_STATIC */ -int LibSensors_init(FILE* input) { +int LibSensors_init(void) { #ifdef BUILD_STATIC - return sym_sensors_init(input); + return sym_sensors_init(NULL); #else @@ -69,7 +69,7 @@ int LibSensors_init(FILE* input) { #undef resolve } - return sym_sensors_init(input); + return sym_sensors_init(NULL); dlfailure: @@ -99,6 +99,18 @@ void LibSensors_cleanup(void) { #endif /* BUILD_STATIC */ } +int LibSensors_reload(void) { +#ifndef BUILD_STATIC + if (!dlopenHandle) { + errno = ENOTSUP; + return -1; + } +#endif /* !BUILD_STATIC */ + + sym_sensors_cleanup(); + return sym_sensors_init(NULL); +} + static int tempDriverPriority(const sensors_chip_name* chip) { static const struct TempDriverDefs { const char* prefix; @@ -120,10 +132,10 @@ static int tempDriverPriority(const sensors_chip_name* chip) { return -1; } -void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { - assert(cpuCount > 0 && cpuCount < 16384); - double data[cpuCount + 1]; - for (size_t i = 0; i < cpuCount + 1; i++) +void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs) { + assert(existingCPUs > 0 && existingCPUs < 16384); + double data[existingCPUs + 1]; + for (size_t i = 0; i < existingCPUs + 1; i++) data[i] = NAN; #ifndef BUILD_STATIC @@ -145,7 +157,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { if (priority < topPriority) { /* Clear data from lower priority sensor */ - for (size_t i = 0; i < cpuCount + 1; i++) + for (size_t i = 0; i < existingCPUs + 1; i++) data[i] = NAN; } @@ -166,7 +178,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { /* Feature name IDs start at 1, adjust to start at 0 to match data indices */ tempID--; - if (tempID > cpuCount) + if (tempID > existingCPUs) continue; const sensors_subfeature* subFeature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT); @@ -190,8 +202,8 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { } /* Adjust data for chips not providing a platform temperature */ - if (coreTempCount + 1 == cpuCount || coreTempCount + 1 == cpuCount / 2) { - memmove(&data[1], &data[0], cpuCount * sizeof(*data)); + if (coreTempCount + 1 == activeCPUs || coreTempCount + 1 == activeCPUs / 2) { + memmove(&data[1], &data[0], existingCPUs * sizeof(*data)); data[0] = NAN; coreTempCount++; @@ -200,7 +212,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { /* Only package temperature - copy to all cores */ if (coreTempCount == 0 && !isnan(data[0])) { - for (unsigned int i = 1; i <= cpuCount; i++) + for (unsigned int i = 1; i <= existingCPUs; i++) data[i] = data[0]; /* No further adjustments */ @@ -210,7 +222,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { /* No package temperature - set to max core temperature */ if (isnan(data[0]) && coreTempCount != 0) { double maxTemp = NAN; - for (unsigned int i = 1; i <= cpuCount; i++) { + for (unsigned int i = 1; i <= existingCPUs; i++) { if (isnan(data[i])) continue; @@ -224,7 +236,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { /* Only temperature for core 0, maybe Ryzen - copy to all other cores */ if (coreTempCount == 1 && !isnan(data[1])) { - for (unsigned int i = 2; i <= cpuCount; i++) + for (unsigned int i = 2; i <= existingCPUs; i++) data[i] = data[1]; /* No further adjustments */ @@ -232,7 +244,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { } /* Half the temperatures, probably HT/SMT - copy to second half */ - const unsigned int delta = cpuCount / 2; + const unsigned int delta = activeCPUs / 2; if (coreTempCount == delta) { memcpy(&data[delta + 1], &data[1], delta * sizeof(*data)); @@ -241,7 +253,7 @@ void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { } out: - for (unsigned int i = 0; i <= cpuCount; i++) + for (unsigned int i = 0; i <= existingCPUs; i++) cpus[i].temperature = data[i]; } diff --git a/linux/LibSensors.h b/linux/LibSensors.h index f3410175..a4c26e20 100644 --- a/linux/LibSensors.h +++ b/linux/LibSensors.h @@ -8,9 +8,10 @@ #include "linux/LinuxProcessList.h" -int LibSensors_init(FILE* input); +int LibSensors_init(void); void LibSensors_cleanup(void); +int LibSensors_reload(void); -void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount); +void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs); #endif /* HEADER_LibSensors */ diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 406216f6..9fd07051 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -158,30 +158,85 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { #endif -static void LinuxProcessList_updateCPUcount(ProcessList* super, FILE* stream) { +static void LinuxProcessList_updateCPUcount(ProcessList* super) { + /* Similiar 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 + */ + LinuxProcessList* this = (LinuxProcessList*) super; + unsigned int existing = 0, active = 0; - unsigned int cpus = 0; - char buffer[PROC_LINE_LENGTH + 1]; - while (fgets(buffer, sizeof(buffer), stream)) { - if (String_startsWith(buffer, "cpu")) { - cpus++; - } + DIR* dir = opendir("/sys/devices/system/cpu"); + if (!dir) { + super->activeCPUs = 1; + super->existingCPUs = 1; + this->cpuData = xReallocArray(this->cpuData, 2, sizeof(CPUData)); + this->cpuData[0].online = true; + this->cpuData[1].online = true; + return; } - if (cpus == 0) - CRT_fatalError("No cpu entry in " PROCSTATFILE); - if (cpus == 1) - CRT_fatalError("No cpu aggregate or cpuN entry in " PROCSTATFILE); + unsigned int currExisting = super->existingCPUs; + + const struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type != DT_DIR) + continue; + + if (!String_startsWith(entry->d_name, "cpu")) + continue; + + char *endp; + unsigned long int id = strtoul(entry->d_name + 3, &endp, 10); + if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0') + continue; - /* Subtract aggregate cpu entry */ - cpus--; +#ifdef HAVE_OPENAT + int cpuDirFd = openat(dirfd(dir), entry->d_name, O_DIRECTORY | O_PATH | O_NOFOLLOW); + if (cpuDirFd < 0) + continue; +#else + char cpuDirFd[4096]; + xSnprintf(cpuDirFd, sizeof(cpuDirFd), "/sys/devices/system/cpu/%s", entry->d_name); +#endif + + existing++; - if (cpus != super->cpuCount || !this->cpus) { - super->cpuCount = MAXIMUM(cpus, 1); - free(this->cpus); - this->cpus = xCalloc(cpus + 1, sizeof(CPUData)); + /* readdir() iterates with no specific order */ + unsigned int max = MAXIMUM(existing, id + 1); + if (max > currExisting) { + this->cpuData = xReallocArray(this->cpuData, max + /* aggregate */ 1, sizeof(CPUData)); + for (unsigned int j = currExisting; j < max; j++) { + this->cpuData[j].online = false; + } + currExisting = max; + } + + char buffer[8]; + ssize_t res = xReadfileat(cpuDirFd, "online", buffer, sizeof(buffer)); + /* If the file "online" does not exist or on failure count as active */ + if (res < 1 || buffer[0] != '0') { + active++; + this->cpuData[id + 1].online = true; + } else { + this->cpuData[id + 1].online = false; + } + + Compat_openatArgClose(cpuDirFd); } + + closedir(dir); + +#ifdef HAVE_SENSORS_SENSORS_H + /* When started with offline CPUs, libsensors does not monitor those, + * even when they become online. */ + if (super->existingCPUs != 0 && (active > super->activeCPUs || currExisting > super->existingCPUs)) + LibSensors_reload(); +#endif + + super->activeCPUs = active; + assert(existing == currExisting); + super->existingCPUs = currExisting; } ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { @@ -220,15 +275,13 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H CRT_fatalError("Failed to parse btime from " PROCSTATFILE); } + fclose(statfile); + if (btime == -1) CRT_fatalError("No btime in " PROCSTATFILE); - rewind(statfile); - // Initialize CPU count - LinuxProcessList_updateCPUcount(pl, statfile); - - fclose(statfile); + LinuxProcessList_updateCPUcount(pl); return pl; } @@ -236,7 +289,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H void ProcessList_delete(ProcessList* pl) { LinuxProcessList* this = (LinuxProcessList*) pl; ProcessList_done(pl); - free(this->cpus); + free(this->cpuData); if (this->ttyDrivers) { for (int i = 0; this->ttyDrivers[i].path; i++) { free(this->ttyDrivers[i].path); @@ -1270,9 +1323,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ return false; } - unsigned int cpus = pl->cpuCount; - bool hideKernelThreads = settings->hideKernelThreads; - bool hideUserlandThreads = settings->hideUserlandThreads; + const unsigned int activeCPUs = pl->activeCPUs; + const bool hideKernelThreads = settings->hideKernelThreads; + const bool hideUserlandThreads = settings->hideUserlandThreads; while ((entry = readdir(dir)) != NULL) { const char* name = entry->d_name; @@ -1407,7 +1460,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ /* period might be 0 after system sleep */ float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0); - proc->percent_cpu = CLAMP(percent_cpu, 0.0F, cpus * 100.0F); + proc->percent_cpu = CLAMP(percent_cpu, 0.0F, activeCPUs * 100.0F); proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0; if (! LinuxProcessList_updateUser(pl, proc, procFd)) @@ -1771,33 +1824,50 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { LinuxProcessList* this = (LinuxProcessList*) super; + LinuxProcessList_updateCPUcount(super); + FILE* file = fopen(PROCSTATFILE, "r"); if (!file) CRT_fatalError("Cannot open " PROCSTATFILE); - LinuxProcessList_updateCPUcount(super, file); - - rewind(file); + unsigned int existingCPUs = super->existingCPUs; + unsigned int lastAdjCpuId = 0; - unsigned int cpus = super->cpuCount; - for (unsigned int i = 0; i <= cpus; i++) { + for (unsigned int i = 0; i <= existingCPUs; i++) { char buffer[PROC_LINE_LENGTH + 1]; unsigned long long int usertime, nicetime, systemtime, idletime; unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0; - // Depending on your kernel version, - // 5, 7, 8 or 9 of these fields will be set. - // The rest will remain at zero. + const char* ok = fgets(buffer, sizeof(buffer), file); if (!ok) break; + // cpu fields are sorted first + if (!String_startsWith(buffer, "cpu")) + break; |