summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Göttsche <cgzones@googlemail.com>2021-06-12 18:17:28 +0200
committerBenny Baumann <BenBE@geshi.org>2021-07-18 07:44:02 +0200
commit41af31be7ffbd34518b27aad56a4f54af6a8adf3 (patch)
tree6e86813a332e93f7ee22b873d7522fa766b4af97
parentc9abd788b17ab28424961310b7e224498510780c (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.c2
-rw-r--r--Affinity.c4
-rw-r--r--AffinityPanel.c6
-rw-r--r--AvailableMetersPanel.c4
-rw-r--r--CPUMeter.c21
-rw-r--r--CommandLine.c2
-rw-r--r--LoadAverageMeter.c12
-rw-r--r--Process.c2
-rw-r--r--ProcessList.c3
-rw-r--r--ProcessList.h3
-rw-r--r--TasksMeter.c2
-rw-r--r--darwin/DarwinProcessList.c8
-rw-r--r--darwin/Platform.c12
-rw-r--r--dragonflybsd/DragonFlyBSDProcessList.c10
-rw-r--r--dragonflybsd/Platform.c2
-rw-r--r--freebsd/FreeBSDProcessList.c10
-rw-r--r--freebsd/Platform.c2
-rw-r--r--linux/LibSensors.c44
-rw-r--r--linux/LibSensors.h5
-rw-r--r--linux/LinuxProcessList.c170
-rw-r--r--linux/LinuxProcessList.h5
-rw-r--r--linux/Platform.c10
-rw-r--r--openbsd/OpenBSDProcessList.c29
-rw-r--r--openbsd/Platform.c18
-rw-r--r--pcp/PCPProcessList.c19
-rw-r--r--solaris/Platform.c2
-rw-r--r--solaris/SolarisProcessList.c14
-rw-r--r--unsupported/UnsupportedProcessList.c3
28 files changed, 276 insertions, 148 deletions
diff --git a/Action.c b/Action.c
index 21183e0d..86468635 100644
--- a/Action.c
+++ b/Action.c
@@ -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))
diff --git a/Affinity.c b/Affinity.c
index 5a73793b..d48fc9bf 100644
--- a/Affinity.c
+++ b/Affinity.c
@@ -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));
diff --git a/CPUMeter.c b/CPUMeter.c
index 0bea79fd..4a061037 100644
--- a/CPUMeter.c
+++ b/CPUMeter.c
@@ -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]);
diff --git a/Process.c b/Process.c
index 9fc76822..fdc17cb2 100644
--- a/Process.c
+++ b/Process.c
@@ -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;