diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c597627c..617ed305 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Bootstrap run: ./autogen.sh - name: Configure - run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --disable-sensors + run: ./configure --enable-werror --enable-affinity --disable-unicode --disable-sensors - name: Enable compatibility modes run: | sed -i 's/#define HAVE_FSTATAT 1/#undef HAVE_FSTATAT/g' config.h @@ -26,7 +26,7 @@ jobs: - name: Build run: make -k - name: Distcheck - run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --disable-sensors" + run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-affinity --disable-unicode --disable-sensors" build-ubuntu-latest-minimal-clang: runs-on: ubuntu-latest @@ -44,11 +44,11 @@ jobs: - name: Bootstrap run: ./autogen.sh - name: Configure - run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --disable-sensors + run: ./configure --enable-werror --enable-affinity --disable-unicode --disable-sensors - name: Build run: make -k - name: Distcheck - run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --disable-sensors" + run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-affinity --disable-unicode --disable-sensors" build-ubuntu-latest-full-featured-gcc: runs-on: ubuntu-latest diff --git a/Action.c b/Action.c index 21183e0d..62cb4525 100644 --- a/Action.c +++ b/Action.c @@ -34,7 +34,7 @@ in the source distribution for its full text. #include "Vector.h" #include "XUtils.h" -#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) +#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)) #include "Affinity.h" #include "AffinityPanel.h" #endif @@ -302,10 +302,10 @@ 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)) +#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)) const Process* p = (const Process*) Panel_getSelected((Panel*)st->mainPanel); if (!p) return HTOP_OK; @@ -328,8 +328,11 @@ static Htop_Reaction actionSetAffinity(State* st) { Affinity_delete(affinity2); } Object_delete(affinityPanel); -#endif return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; +#else + return HTOP_OK; +#endif + } static Htop_Reaction actionKill(State* st) { @@ -484,7 +487,7 @@ static const struct { { .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" }, { .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" }, { .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" }, -#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) +#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)) { .key = " a: ", .roInactive = true, .info = "set CPU affinity" }, #endif { .key = " e: ", .roInactive = false, .info = "show process environment" }, diff --git a/Affinity.c b/Affinity.c index 5a73793b..13f5547c 100644 --- a/Affinity.c +++ b/Affinity.c @@ -22,7 +22,7 @@ in the source distribution for its full text. #else #define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS #endif -#elif defined(HAVE_LINUX_AFFINITY) +#elif defined(HAVE_AFFINITY) #include #endif @@ -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 { @@ -84,7 +84,7 @@ bool Affinity_set(Process* proc, Arg arg) { return ok; } -#elif defined(HAVE_LINUX_AFFINITY) +#elif defined(HAVE_AFFINITY) Affinity* Affinity_get(const Process* proc, ProcessList* pl) { cpu_set_t cpuset; @@ -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/Affinity.h b/Affinity.h index d28e848b..cf08cce7 100644 --- a/Affinity.h +++ b/Affinity.h @@ -12,7 +12,7 @@ in the source distribution for its full text. #include "ProcessList.h" -#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY) +#if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY) #include #include "Object.h" @@ -20,8 +20,8 @@ in the source distribution for its full text. #endif -#if defined(HAVE_LIBHWLOC) && defined(HAVE_LINUX_AFFINITY) -#error hwloc and linux affinity are mutual exclusive. +#if defined(HAVE_LIBHWLOC) && defined(HAVE_AFFINITY) +#error hwloc and affinity support are mutual exclusive. #endif @@ -38,12 +38,12 @@ void Affinity_delete(Affinity* this); void Affinity_add(Affinity* this, unsigned int id); -#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY) +#if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY) Affinity* Affinity_get(const Process* proc, ProcessList* pl); bool Affinity_set(Process* proc, Arg arg); -#endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */ +#endif /* HAVE_LIBHWLOC || HAVE_AFFINITY */ #endif diff --git a/AffinityPanel.c b/AffinityPanel.c index 25169994..dbce5ca0 100644 --- a/AffinityPanel.c +++ b/AffinityPanel.c @@ -383,7 +383,10 @@ 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++) { + if (!ProcessList_isCPUonline(this->pl, i)) + continue; + char number[16]; xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i)); unsigned cpu_width = 4 + strlen(number); @@ -427,7 +430,7 @@ 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 (int i = 0; i < Vector_size(this->cpuids); i++) { 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 323b02ff..dfb3f7ba 100644 --- a/Process.c +++ b/Process.c @@ -824,7 +824,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..93958cbb 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -83,12 +83,15 @@ typedef struct ProcessList_ { memory_t usedSwap; memory_t cachedSwap; - unsigned int cpuCount; + unsigned int activeCPUs; + unsigned int existingCPUs; } ProcessList; +/* Implemented by platforms */ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); diff --git a/README b/README index c38818ab..121ef964 100644 --- a/README +++ b/README @@ -80,8 +80,11 @@ To install on the local system run `make install`. By default `make install` ins enable Performance Co-Pilot support via a new pcp-htop utility dependency: *libpcp* default: *no* + * `--enable-affinity`: + enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc + default: *check* * `--enable-hwloc`: - enable hwloc support for CPU affinity; disables Linux affinity + enable hwloc support for CPU affinity; disables affinity support dependency: *libhwloc* default: *no* * `--enable-static`: @@ -113,9 +116,6 @@ To install on the local system run `make install`. By default `make install` ins * `--enable-ancient-vserver`: enable ancient VServer support (implies `--enable-vserver`) default: *no* - * `--enable-linux-affinity`: - enable Linux `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc - default: *check* * `--enable-delayacct`: enable Linux delay accounting support dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3* 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/UptimeMeter.c b/UptimeMeter.c index 51ceef8c..9036e539 100644 --- a/UptimeMeter.c +++ b/UptimeMeter.c @@ -19,7 +19,7 @@ static const int UptimeMeter_attributes[] = { static void UptimeMeter_updateValues(Meter* this) { int totalseconds = Platform_getUptime(); - if (totalseconds == -1) { + if (totalseconds <= 0) { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "(unknown)"); return; } diff --git a/configure.ac b/configure.ac index be40187a..1c83c28e 100644 --- a/configure.ac +++ b/configure.ac @@ -355,9 +355,44 @@ AC_CHECK_FUNCS( [set_escdelay] ) AC_CHECK_FUNCS( [getmouse] ) +AC_ARG_ENABLE([affinity], + [AS_HELP_STRING([--enable-affinity], + [enable sched_setaffinity and sched_getaffinity for affinity support, conflicts with hwloc @<:@default=check@:>@])], + [], + [enable_affinity=check]) +if test "x$enable_affinity" = xcheck; then + if test "x$enable_hwloc" = xyes; then + enable_affinity=no + else + AC_MSG_CHECKING([for usable sched_setaffinity]) + AC_RUN_IFELSE([ + AC_LANG_PROGRAM([[ + #include + #include + static cpu_set_t cpuset; + ]], [[ + CPU_ZERO(&cpuset); + sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); + if (errno == ENOSYS) return 1; + ]])], + [enable_affinity=yes + AC_MSG_RESULT([yes])], + [enable_affinity=no + AC_MSG_RESULT([no])], + [AC_MSG_RESULT([yes (assumed while cross compiling)])]) + fi +fi +if test "x$enable_affinity" = xyes; then + if test "x$enable_hwloc" = xyes; then + AC_MSG_ERROR([--enable-hwloc and --enable-affinity are mutual exclusive. Specify at most one of them.]) + fi + AC_DEFINE([HAVE_AFFINITY], [1], [Define if sched_setaffinity and sched_getaffinity are to be used.]) +fi + + AC_ARG_ENABLE([hwloc], [AS_HELP_STRING([--enable-hwloc], - [enable hwloc support for CPU affinity; disables Linux affinity; requires libhwloc @<:@default=no@:>@])], + [enable hwloc support for CPU affinity; disables affinity support; requires libhwloc @<:@default=no@:>@])], [], [enable_hwloc=no]) case "$enable_hwloc" in @@ -433,41 +468,6 @@ if test "x$enable_ancient_vserver" = xyes; then fi -AC_ARG_ENABLE([linux_affinity], - [AS_HELP_STRING([--enable-linux-affinity], - [enable Linux sched_setaffinity and sched_getaffinity for affinity support, conflicts with hwloc @<:@default=check@:>@])], - [], - [enable_linux_affinity=check]) -if test "x$enable_linux_affinity" = xcheck; then - if test "x$enable_hwloc" = xyes; then - enable_linux_affinity=no - else - AC_MSG_CHECKING([for usable sched_setaffinity]) - AC_RUN_IFELSE([ - AC_LANG_PROGRAM([[ - #include - #include - static cpu_set_t cpuset; - ]], [[ - CPU_ZERO(&cpuset); - sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); - if (errno == ENOSYS) return 1; - ]])], - [enable_linux_affinity=yes - AC_MSG_RESULT([yes])], - [enable_linux_affinity=no - AC_MSG_RESULT([no])], - [AC_MSG_RESULT([yes (assumed while cross compiling)])]) - fi -fi -if test "x$enable_linux_affinity" = xyes; then - if test "x$enable_hwloc" = xyes; then - AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.]) - fi - AC_DEFINE([HAVE_LINUX_AFFINITY], [1], [Define if Linux sched_setaffinity and sched_getaffinity are to be used.]) -fi - - AC_ARG_ENABLE([capabilities], [AS_HELP_STRING([--enable-capabilities], [enable Linux capabilities support; requires libcap @<:@default=check@:>@])], @@ -688,11 +688,11 @@ AC_MSG_RESULT([ (Linux) openvz: $enable_openvz (Linux) vserver: $enable_vserver (Linux) ancient vserver: $enable_ancient_vserver - (Linux) affinity: $enable_linux_affinity (Linux) delay accounting: $enable_delayacct (Linux) sensors: $enable_sensors (Linux) capabilities: $enable_capabilities unicode: $enable_unicode + affinity: $enable_affinity hwloc: $enable_hwloc debug: $enable_debug static: $enable_static diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 91d62c4a..8d14efe1 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; @@ -234,3 +236,12 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { free(ps); } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) super; (void) id; + + return true; +} diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h index bd39ab0e..24259d3e 100644 --- a/darwin/DarwinProcessList.h +++ b/darwin/DarwinProcessList.h @@ -34,4 +34,6 @@ void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif diff --git a/darwin/Platform.c b/darwin/Platform.c index df7e133f..d4d15dad 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..9b1bb41f 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); @@ -430,7 +432,6 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { int count = 0; - // TODO Kernel Threads seem to be skipped, need to figure out the correct flag const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); for (int i = 0; i < count; i++) { @@ -442,8 +443,6 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { Process* proc = ProcessList_getProcess(super, kproc->kp_ktaddr ? (pid_t)kproc->kp_ktaddr : kproc->kp_pid, &preExisting, DragonFlyBSDProcess_new); DragonFlyBSDProcess* dfp = (DragonFlyBSDProcess*) proc; - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); - if (!preExisting) { dfp->jid = kproc->kp_jailid; if (kproc->kp_ktaddr && kproc->kp_flags & P_SYSTEM) { @@ -596,6 +595,16 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (proc->state == 'R') super->runningTasks++; + proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); proc->updated = true; } } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) super; (void) id; + + return true; +} diff --git a/dragonflybsd/DragonFlyBSDProcessList.h b/dragonflybsd/DragonFlyBSDProcessList.h index b9c9b5c8..626d2b24 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.h +++ b/dragonflybsd/DragonFlyBSDProcessList.h @@ -59,4 +59,6 @@ void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif 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..8f8560a2 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); @@ -378,16 +380,15 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { } static void FreeBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process* proc) { - const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, kproc->ki_pid }; - char buffer[2048]; - size_t size = sizeof(buffer); - if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) { + if (Process_isKernelThread(proc)) { Process_updateExe(proc, NULL); return; } - /* Kernel threads return an empty buffer */ - if (buffer[0] == '\0') { + const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, kproc->ki_pid }; + char buffer[2048]; + size_t size = sizeof(buffer); + if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) { Process_updateExe(proc, NULL); return; } @@ -494,12 +495,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { Process* proc = ProcessList_getProcess(super, kproc->ki_pid, &preExisting, FreeBSDProcess_new); FreeBSDProcess* fp = (FreeBSDProcess*) proc; - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); - if (!preExisting) { fp->jid = kproc->ki_jid; proc->pid = kproc->ki_pid; - proc->isKernelThread = kproc->ki_pid != 0 && kproc->ki_pid != 1 && (kproc->ki_flag & P_SYSTEM); + proc->isKernelThread = kproc->ki_pid != 1 && (kproc->ki_flag & P_SYSTEM); proc->isUserlandThread = false; proc->ppid = kproc->ki_ppid; proc->tpgid = kproc->ki_tpgid; @@ -591,9 +590,20 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (Process_isKernelThread(proc)) super->kernelThreads++; + proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); + super->totalTasks++; if (proc->state == 'R') super->runningTasks++; proc->updated = true; } } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) super; (void) id; + + return true; +} diff --git a/freebsd/FreeBSDProcessList.h b/freebsd/FreeBSDProcessList.h index ac6bcd0a..7efcda92 100644 --- a/freebsd/FreeBSDProcessList.h +++ b/freebsd/FreeBSDProcessList.h @@ -53,4 +53,6 @@ void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif 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..e5d38e5a 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; + } + + 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; + +#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++; + + /* 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); } - if (cpus == 0) - CRT_fatalError("No cpu entry in " PROCSTATFILE); - if (cpus == 1) - CRT_fatalError("No cpu aggregate or cpuN entry in " PROCSTATFILE); + closedir(dir); - /* Subtract aggregate cpu entry */ - cpus--; +#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 - if (cpus != super->cpuCount || !this->cpus) { - super->cpuCount = MAXIMUM(cpus, 1); - free(this->cpus); - this->cpus = xCalloc(cpus + 1, sizeof(CPUData)); - } + 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); + unsigned int existingCPUs = super->existingCPUs; + unsigned int lastAdjCpuId = 0; - rewind(file); - - 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; + + // Depending on your kernel version, + // 5, 7, 8 or 9 of these fields will be set. + // The rest will remain at zero. + unsigned int adjCpuId; if (i == 0) { (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); + adjCpuId = 0; } else { unsigned int cpuid; (void) sscanf(buffer, "cpu%4u %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); - assert(cpuid == i - 1); + adjCpuId = cpuid + 1; } + + if (adjCpuId > super->existingCPUs) + break; + + for (unsigned int j = lastAdjCpuId + 1; j < adjCpuId; j++) { + // Skipped an ID, but /proc/stat is ordered => got offline CPU + memset(&(this->cpuData[j]), '\0', sizeof(CPUData)); + } + lastAdjCpuId = adjCpuId; + // Guest time is already accounted in usertime usertime -= guest; nicetime -= guestnice; @@ -1807,7 +1877,7 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { unsigned long long int systemalltime = systemtime + irq + softIrq; unsigned long long int virtalltime = guest + guestnice; unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; - CPUData* cpuData = &(this->cpus[i]); + CPUData* cpuData = &(this->cpuData[adjCpuId]); // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() // used in /proc/stat rounds down numbers, it can lead to a case where the // integer overflow. @@ -1837,7 +1907,7 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { cpuData->totalTime = totaltime; } - double period = (double)this->cpus[0].totalPeriod / cpus; + double period = (double)this->cpuData[0].totalPeriod / super->activeCPUs; char buffer[PROC_LINE_LENGTH + 1]; while (fgets(buffer, sizeof(buffer), file)) { @@ -1853,7 +1923,7 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { } static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) { - unsigned int cpus = this->super.cpuCount; + unsigned int existingCPUs = this->super.existingCPUs; int numCPUsWithFrequency = 0; unsigned long totalFrequency = 0; @@ -1871,7 +1941,7 @@ static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) { return -1; } - for (unsigned int i = 0; i < cpus; ++i) { + for (unsigned int i = 0; i < existingCPUs; ++i) { char pathBuffer[64]; xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i); @@ -1887,7 +1957,7 @@ static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) { if (fscanf(file, "%lu", &frequency) == 1) { /* convert kHz to MHz */ frequency = frequency / 1000; - this->cpus[i + 1].frequency = frequency; + this->cpuData[i + 1].frequency = frequency; numCPUsWithFrequency++; totalFrequency += frequency; } @@ -1907,7 +1977,7 @@ static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) { } if (numCPUsWithFrequency > 0) - this->cpus[0].frequency = (double)totalFrequency / numCPUsWithFrequency; + this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency; return 0; } @@ -1917,7 +1987,7 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) { if (file == NULL) return; - unsigned int cpus = this->super.cpuCount; + unsigned int existingCPUs = this->super.existingCPUs; int numCPUsWithFrequency = 0; double totalFrequency = 0; int cpuid = -1; @@ -1940,11 +2010,11 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) { (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) || (sscanf(buffer, "clock: %lfMHz", &frequency) == 1) ) { - if (cpuid < 0 || (unsigned int)cpuid > (cpus - 1)) { + if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) { continue; } - CPUData* cpuData = &(this->cpus[cpuid + 1]); + CPUData* cpuData = &(this->cpuData[cpuid + 1]); /* do not override sysfs data */ if (isnan(cpuData->frequency)) { cpuData->frequency = frequency; @@ -1958,15 +2028,15 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) { fclose(file); if (numCPUsWithFrequency > 0) { - this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency; + this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency; } } static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { - unsigned int cpus = this->super.cpuCount; + unsigned int existingCPUs = this->super.existingCPUs; - for (unsigned int i = 0; i <= cpus; i++) { - this->cpus[i].frequency = NAN; + for (unsigned int i = 0; i <= existingCPUs; i++) { + this->cpuData[i].frequency = NAN; } if (scanCPUFreqencyFromSysCPUFreq(this) == 0) { @@ -1993,7 +2063,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { #ifdef HAVE_SENSORS_SENSORS_H if (settings->showCPUTemperature) - LibSensors_getCPUTemperatures(this->cpus, this->super.cpuCount); + LibSensors_getCPUTemperatures(this->cpuData, this->super.existingCPUs, this->super.activeCPUs); #endif // in pause mode only gather global data for meters (CPU/memory/...) @@ -2011,3 +2081,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period); } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + const LinuxProcessList* this = (const LinuxProcessList*) super; + return this->cpuData[id + 1].online; +} diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 555b5c41..2f296eca 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -53,6 +53,8 @@ typedef struct CPUData_ { #ifdef HAVE_SENSORS_SENSORS_H double temperature; #endif + + bool online; } CPUData; typedef struct TtyDriver_ { @@ -65,7 +67,8 @@ typedef struct TtyDriver_ { typedef struct LinuxProcessList_ { ProcessList super; - CPUData* cpus; + CPUData* cpuData; + TtyDriver* ttyDrivers; bool haveSmapsRollup; @@ -117,4 +120,6 @@ void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif diff --git a/linux/Platform.c b/linux/Platform.c index 2d28b8e1..d23bf57d 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -246,10 +246,16 @@ int Platform_getMaxPid() { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const LinuxProcessList* pl = (const LinuxProcessList*) this->pl; - const CPUData* cpuData = &(pl->cpus[cpu]); + const CPUData* cpuData = &(pl->cpuData[cpu]); double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); double percent; double* v = this->values; + + if (!cpuData->online) { + this->curItems = 0; + return NAN; + } + v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; if (this->pl->settings->detailedCPUTime) { @@ -1000,7 +1006,7 @@ void Platform_init(void) { } #ifdef HAVE_SENSORS_SENSORS_H - LibSensors_init(NULL); + LibSensors_init(); #endif } diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index 76b2aeb7..089ca21d 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -36,13 +36,66 @@ static long fscale; static int pageSize; static int pageSizeKB; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { +static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { + OpenBSDProcessList* opl = (OpenBSDProcessList*) super; const int nmib[] = { CTL_HW, HW_NCPU }; const int mib[] = { CTL_HW, HW_NCPUONLINE }; - const int fmib[] = { CTL_KERN, KERN_FSCALE }; int r; - unsigned int cpu_index_c = 0; - unsigned int ncpu; + unsigned int value; + size_t size; + bool change = false; + + size = sizeof(value); + r = sysctl(mib, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = 1; + } + + if (value != super->activeCPUs) { + super->activeCPUs = value; + change = true; + } + + size = sizeof(value); + r = sysctl(nmib, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = super->activeCPUs; + } + + if (value != super->existingCPUs) { + opl->cpuData = xReallocArray(opl->cpuData, value + 1, sizeof(CPUData)); + super->existingCPUs = value; + change = true; + } + + if (change) { + CPUData* dAvg = &opl->cpuData[0]; + memset(dAvg, '\0', sizeof(CPUData)); + dAvg->totalTime = 1; + dAvg->totalPeriod = 1; + dAvg->online = true; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* d = &opl->cpuData[i + 1]; + memset(d, '\0', sizeof(CPUData)); + d->totalTime = 1; + d->totalPeriod = 1; + + const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i }; + struct cpustats cpu_stats; + + size = sizeof(cpu_stats); + if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) { + CRT_fatalError("ncmib sysctl call failed"); + } + d->online = (cpu_stats.cs_flags & CPUSTATS_ONLINE); + } + } +} + + +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { + const int fmib[] = { CTL_KERN, KERN_FSCALE }; size_t size; char errbuf[_POSIX2_LINE_MAX]; @@ -50,18 +103,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H ProcessList* pl = (ProcessList*) opl; ProcessList_init(pl, Class(OpenBSDProcess), usersTable, dynamicMeters, pidMatchList, userId); - size = sizeof(pl->cpuCount); - r = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0); - if (r < 0 || pl->cpuCount < 1) { - pl->cpuCount = 1; - } - opl->cpus = xCalloc(pl->cpuCount + 1, sizeof(CPUData)); - - size = sizeof(int); - r = sysctl(nmib, 2, &ncpu, &size, NULL, 0); - if (r < 0) { - ncpu = pl->cpuCount; - } + OpenBSDProcessList_updateCPUcount(pl); size = sizeof(fscale); if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { @@ -72,12 +114,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H CRT_fatalError("pagesize sysconf call failed"); pageSizeKB = pageSize / ONE_K; - for (unsigned int i = 0; i <= pl->cpuCount; i++) { - CPUData* d = opl->cpus + i; - d->totalTime = 1; - d->totalPeriod = 1; - } - opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); if (opl->kd == NULL) { CRT_fatalError("kvm_openfiles() failed"); @@ -85,23 +121,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H opl->cpuSpeed = -1; - for (unsigned int i = 0; i < ncpu; i++) { - const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i }; - struct cpustats cpu_stats; - - size = sizeof(cpu_stats); - if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) { - CRT_fatalError("ncmib sysctl call failed"); - } - if (cpu_stats.cs_flags & CPUSTATS_ONLINE) { - opl->cpus[cpu_index_c].cpuIndex = i; - cpu_index_c++; - } - - if (cpu_index_c == pl->cpuCount) - break; - } - return pl; } @@ -112,7 +131,7 @@ void ProcessList_delete(ProcessList* this) { kvm_close(opl->kd); } - free(opl->cpus); + free(opl->cpuData); ProcessList_done(this); free(this); @@ -172,7 +191,7 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { } static void OpenBSDProcessList_updateCwd(const struct kinfo_proc* kproc, Process* proc) { - const int mib[] = { CTL_KERN, KERN_PROC_CWD, kproc->ki_pid }; + const int mib[] = { CTL_KERN, KERN_PROC_CWD, kproc->p_pid }; char buffer[2048]; size_t size = sizeof(buffer); if (sysctl(mib, 3, buffer, &size, NULL, 0) != 0) { @@ -274,8 +293,6 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { Process* proc = ProcessList_getProcess(&this->super, (kproc->p_tid == -1) ? kproc->p_pid : kproc->p_tid, &preExisting, OpenBSDProcess_new); OpenBSDProcess* fp = (OpenBSDProcess*) proc; - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); - if (!preExisting) { proc->ppid = kproc->p_ppid; proc->tpgid = kproc->p_tpgid; @@ -312,7 +329,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { proc->m_virt = kproc->p_vm_dsize * pageSizeKB; proc->m_resident = kproc->p_vm_rssize * pageSizeKB; proc->percent_mem = proc->m_resident / (float)this->super.totalMem * 100.0F; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0F, this->super.cpuCount * 100.0F); + proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0F, this->super.activeCPUs * 100.0F); proc->nice = kproc->p_nice - 20; proc->time = 100 * (kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000)); proc->priority = kproc->p_priority - PZERO; @@ -347,11 +364,13 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { if (proc->state == 'R') { this->super.runningTasks++; } + + proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); proc->updated = true; } } -static void getKernelCPUTimes(int cpuId, u_int64_t* times) { +static void getKernelCPUTimes(unsigned int cpuId, u_int64_t* times) { const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId }; size_t length = sizeof(*times) * CPUSTATES; if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { @@ -400,9 +419,14 @@ static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { u_int64_t kernelTimes[CPUSTATES] = {0}; u_int64_t avg[CPUSTATES] = {0}; - for (unsigned int i = 0; i < this->super.cpuCount; i++) { - getKernelCPUTimes(this->cpus[i].cpuIndex, kernelTimes); - CPUData* cpu = this->cpus + i + 1; + for (unsigned int i = 0; i < this->super.existingCPUs; i++) { + CPUData* cpu = &this->cpuData[i + 1]; + + if (!cpu->online) { + continue; + } + + getKernelCPUTimes(i, kernelTimes); kernelCPUTimesToHtop(kernelTimes, cpu); avg[CP_USER] += cpu->userTime; @@ -416,10 +440,10 @@ static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { } for (int i = 0; i < CPUSTATES; i++) { - avg[i] /= this->super.cpuCount; + avg[i] /= this->super.activeCPUs; } - kernelCPUTimesToHtop(avg, this->cpus); + kernelCPUTimesToHtop(avg, &this->cpuData[0]); { const int mib[] = { CTL_HW, HW_CPUSPEED }; @@ -436,6 +460,7 @@ static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { OpenBSDProcessList* opl = (OpenBSDProcessList*) super; + OpenBSDProcessList_updateCPUcount(super); OpenBSDProcessList_scanMemoryInfo(super); OpenBSDProcessList_scanCPUTime(opl); @@ -446,3 +471,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { OpenBSDProcessList_scanProcs(opl); } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + const OpenBSDProcessList* opl = (const OpenBSDProcessList*) super; + return opl->cpuData[id + 1].online; +} diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h index 5734d3ab..0a47773f 100644 --- a/openbsd/OpenBSDProcessList.h +++ b/openbsd/OpenBSDProcessList.h @@ -36,14 +36,14 @@ typedef struct CPUData_ { unsigned long long int intrPeriod; unsigned long long int idlePeriod; - int cpuIndex; + bool online; } CPUData; typedef struct OpenBSDProcessList_ { ProcessList super; kvm_t* kd; - CPUData* cpus; + CPUData* cpuData; int cpuSpeed; } OpenBSDProcessList; @@ -55,4 +55,6 @@ void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 1afbdfde..65133122 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -169,11 +169,18 @@ int Platform_getMaxPid() { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const OpenBSDProcessList* pl = (const OpenBSDProcessList*) this->pl; - const CPUData* cpuData = &(pl->cpus[cpu]); - double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; + const CPUData* cpuData = &(pl->cpuData[cpu]); + double total; double totalPercent; double* v = this->values; + if (!cpuData->online) { + this->curItems = 0; + return NAN; + } + + total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; + v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; if (this->pl->settings->detailedCPUTime) { diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index 2731a8ae..ae974435 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -33,10 +33,13 @@ static int PCPProcessList_computeCPUcount(void) { static void PCPProcessList_updateCPUcount(PCPProcessList* this) { ProcessList* pl = &(this->super); unsigned int cpus = PCPProcessList_computeCPUcount(); - if (cpus == pl->cpuCount) + if (cpus == pl->existingCPUs) return; - pl->cpuCount = cpus; + pl->existingCPUs = cpus; + // TODO: support offline CPUs and hot swapping + pl->activeCPUs = pl->existingCPUs; + free(this->percpu); free(this->values); @@ -79,7 +82,7 @@ void ProcessList_delete(ProcessList* pl) { PCPProcessList* this = (PCPProcessList*) pl; ProcessList_done(pl); free(this->values); - for (unsigned int i = 0; i < pl->cpuCount; i++) + for (unsigned int i = 0; i < pl->existingCPUs; i++) free(this->percpu[i]); free(this->percpu); free(this->cpu); @@ -372,7 +375,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, float percent_cpu = (pp->utime + pp->stime - lasttimes) / period * 100.0; proc->percent_cpu = isnan(percent_cpu) ? - 0.0 : CLAMP(percent_cpu, 0.0, pl->cpuCount * 100.0); + 0.0 : CLAMP(percent_cpu, 0.0, pl->activeCPUs * 100.0); proc->percent_mem = proc->m_resident / (double)pl->totalMem * 100.0; PCPProcessList_updateUsername(proc, pid, offset, pl->usersTable); @@ -538,7 +541,7 @@ static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, Metric metric, static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, Metric metric, CPUMetric cpumetric) { - int cpus = this->super.cpuCount; + int cpus = this->super.existingCPUs; if (Metric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL) memset(this->values, 0, cpus * sizeof(pmAtomValue)); for (int i = 0; i < cpus; i++) @@ -547,7 +550,7 @@ static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, Metric metric, static void PCPProcessList_updatePerCPUReal(PCPProcessList* this, Metric metric, CPUMetric cpumetric) { - int cpus = this->super.cpuCount; + int cpus = this->super.existingCPUs; if (Metric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL) memset(this->values, 0, cpus * sizeof(pmAtomValue)); for (int i = 0; i < cpus; i++) @@ -607,7 +610,7 @@ static void PCPProcessList_updateHeader(ProcessList* super, const Settings* sett PCPProcessList_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME); PCPProcessList_deriveCPUTime(this->cpu); - for (unsigned int i = 0; i < super->cpuCount; i++) + for (unsigned int i = 0; i < super->existingCPUs; i++) PCPProcessList_backupCPUTime(this->percpu[i]); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME); @@ -618,7 +621,7 @@ static void PCPProcessList_updateHeader(ProcessList* super, const Settings* sett PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME); - for (unsigned int i = 0; i < super->cpuCount; i++) + for (unsigned int i = 0; i < super->existingCPUs; i++) PCPProcessList_deriveCPUTime(this->percpu[i]); if (settings->showCPUFrequency) @@ -671,3 +674,12 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { double period = (this->timestamp - sample) * 100; PCPProcessList_updateProcesses(this, period, ×tamp); } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) super; (void) id; + + return true; +} diff --git a/pcp/PCPProcessList.h b/pcp/PCPProcessList.h index f1784904..7f0f6fe4 100644 --- a/pcp/PCPProcessList.h +++ b/pcp/PCPProcessList.h @@ -69,4 +69,6 @@ void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif diff --git a/solaris/Platform.c b/solaris/Platform.c index da39a33d..4923b790 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -184,7 +184,7 @@ int Platform_getMaxPid() { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const SolarisProcessList* spl = (const SolarisProcessList*) this->pl; - unsigned int cpus = this->pl->cpuCount; + unsigned int cpus = this->pl->existingCPUs; const CPUData* cpuData = NULL; if (cpus == 1) { @@ -194,6 +194,11 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { cpuData = &(spl->cpus[cpu]); } + if (!cpuData->online) { + this->curItems = 0; + return NAN; + } + double percent; double* v = this->values; diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index daba232b..63b1db80 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -47,32 +47,70 @@ static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sp return zname; } +static void SolarisProcessList_updateCPUcount(ProcessList* super) { + SolarisProcessList* spl = (SolarisProcessList*) super; + long int s; + bool change = false; + + s = sysconf(_SC_NPROCESSORS_CONF); + if (s < 1) + CRT_fatalError("Cannot get exisitng CPU count by sysconf(_SC_NPROCESSORS_CONF)"); + + if (s != super->existingCPUs) { + if (s == 1) { + spl->cpus = xRealloc(spl->cpus, sizeof(CPUData)); + spl->cpus[0].online = true; + } else { + spl->cpus = xReallocArray(spl->cpus, s + 1, sizeof(CPUData)); + for (int i = 0; i < s + 1; i++) { + spl->cpus[i].online = false; + } + } + + change = true; + super->existingCPUs = s; + } + + s = sysconf(_SC_NPROCESSORS_ONLN); + if (s < 1) + CRT_fatalError("Cannot get active CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); + + if (s != super->activeCPUs) { + change = true; + super->activeCPUs = s; + } + + if (change) { + kstat_close(spl->kd); + spl->kd = kstat_open(); + if (!spl->kd) + CRT_fatalError("Cannot open kstat handle"); + } +} + ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); ProcessList* pl = (ProcessList*) spl; ProcessList_init(pl, Class(SolarisProcess), usersTable, dynamicMeters, pidMatchList, userId); spl->kd = kstat_open(); + if (!spl->kd) + CRT_fatalError("Cannot open kstat handle"); pageSize = sysconf(_SC_PAGESIZE); if (pageSize == -1) CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); pageSizeKB = pageSize / 1024; - pl->cpuCount = sysconf(_SC_NPROCESSORS_ONLN); - if (pl->cpuCount == (unsigned int)-1) - CRT_fatalError("Cannot get CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); - else if (pl->cpuCount == 1) - spl->cpus = xRealloc(spl->cpus, sizeof(CPUData)); - else - spl->cpus = xRealloc(spl->cpus, (pl->cpuCount + 1) * sizeof(CPUData)); + SolarisProcessList_updateCPUcount(pl); return pl; } static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { const SolarisProcessList* spl = (SolarisProcessList*) pl; - unsigned int cpus = pl->cpuCount; + unsigned int activeCPUs = pl->activeCPUs; + unsigned int existingCPUs = pl->existingCPUs; kstat_t* cpuinfo = NULL; kstat_named_t* idletime = NULL; kstat_named_t* intrtime = NULL; @@ -85,44 +123,45 @@ static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { double userbuf = 0; int arrskip = 0; - assert(cpus > 0); + assert(existingCPUs > 0); + assert(spl->kd); - if (cpus > 1) { + if (existingCPUs > 1) { // Store values for the stats loop one extra element up in the array // to leave room for the average to be calculated afterwards arrskip++; } // Calculate per-CPU statistics first - for (unsigned int i = 0; i < cpus; i++) { - if (spl->kd != NULL) { - if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu", i, "sys")) != NULL) { - if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { - idletime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_idle"); - intrtime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_intr"); - krnltime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_kernel"); - usertime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_user"); - } + for (unsigned int i = 0; i < existingCPUs; i++) { + CPUData* cpuData = &(spl->cpus[i + arrskip]); + + if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu", i, "sys")) != NULL) { + cpuData->online = true; + if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { + idletime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_idle"); + intrtime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_intr"); + krnltime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_kernel"); + usertime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_user"); } + } else { + cpuData->online = false; + continue; } assert( (idletime != NULL) && (intrtime != NULL) && (krnltime != NULL) && (usertime != NULL) ); if (pl->settings->showCPUFrequency) { - if (spl->kd != NULL) { - if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu_info", i, NULL)) != NULL) { - if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { - cpu_freq = kstat_data_lookup_wrapper(cpuinfo, "current_clock_Hz"); - } + if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu_info", i, NULL)) != NULL) { + if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { + cpu_freq = kstat_data_lookup_wrapper(cpuinfo, "current_clock_Hz"); } } assert( cpu_freq != NULL ); } - CPUData* cpuData = &(spl->cpus[i + arrskip]); - uint64_t totaltime = (idletime->value.ui64 - cpuData->lidle) + (intrtime->value.ui64 - cpuData->lintr) + (krnltime->value.ui64 - cpuData->lkrnl) @@ -143,7 +182,7 @@ static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { // Add frequency in MHz cpuData->frequency = pl->settings->showCPUFrequency ? (double)cpu_freq->value.ui64 / 1E6 : NAN; // Accumulate the current percentages into buffers for later average calculation - if (cpus > 1) { + if (existingCPUs > 1) { userbuf += cpuData->userPercent; krnlbuf += cpuData->systemPercent; intrbuf += cpuData->irqPercent; @@ -151,14 +190,14 @@ static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { } } - if (cpus > 1) { + if (existingCPUs > 1) { CPUData* cpuData = &(spl->cpus[0]); - cpuData->userPercent = userbuf / cpus; + cpuData->userPercent = userbuf / activeCPUs; cpuData->nicePercent = (double)0.0; // Not implemented on Solaris - cpuData->systemPercent = krnlbuf / cpus; - cpuData->irqPercent = intrbuf / cpus; + cpuData->systemPercent = krnlbuf / activeCPUs; + cpuData->irqPercent = intrbuf / activeCPUs; cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; - cpuData->idlePercent = idlebuf / cpus; + cpuData->idlePercent = idlebuf / activeCPUs; } } @@ -479,6 +518,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { + SolarisProcessList_updateCPUcount(super); SolarisProcessList_scanCPUTime(super); SolarisProcessList_scanMemoryInfo(super); SolarisProcessList_scanZfsArcstats(super); @@ -491,3 +531,11 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { super->kernelThreads = 1; proc_walk(&SolarisProcessList_walkproc, super, PR_WALK_LWP); } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + const SolarisProcessList* spl = (const SolarisProcessList*) super; + + return (super->existingCPUs == 1) ? true : spl->cpus[id + 1].online; +} diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h index e97d5cff..bee35cce 100644 --- a/solaris/SolarisProcessList.h +++ b/solaris/SolarisProcessList.h @@ -44,6 +44,7 @@ typedef struct CPUData_ { uint64_t lkrnl; uint64_t lintr; uint64_t lidle; + bool online; } CPUData; typedef struct SolarisProcessList_ { @@ -59,4 +60,6 @@ void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index 3318453f..16c01558 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -18,7 +18,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H ProcessList* this = xCalloc(1, sizeof(ProcessList)); ProcessList_init(this, Class(Process), usersTable, dynamicMeters, pidMatchList, userId); - this->cpuCount = 1; + this->existingCPUs = 1; + this->activeCPUs = 1; return this; } @@ -88,3 +89,11 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (!preExisting) ProcessList_add(super, proc); } + +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { + assert(id < super->existingCPUs); + + (void) super; (void) id; + + return true; +} diff --git a/unsupported/UnsupportedProcessList.h b/unsupported/UnsupportedProcessList.h index 10432758..764a1924 100644 --- a/unsupported/UnsupportedProcessList.h +++ b/unsupported/UnsupportedProcessList.h @@ -16,4 +16,6 @@ void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); + #endif