Merge branch 'upstream-KWSys' into update-kwsys

# By KWSys Upstream
* upstream-KWSys:
  KWSys 2026-09-03 (151e9c02)
This commit is contained in:
Brad King
2026-09-03 08:43:58 -04:00
5 changed files with 297 additions and 27 deletions
+4
View File
@@ -956,6 +956,10 @@ if(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
endif()
if(KWSYS_USE_SystemInformation)
set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testSystemInformation.cxx)
if(KWSYS_USE_SystemTools)
set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS}
testSystemInformationBaseFrequency.cxx)
endif()
endif()
if(KWSYS_USE_DynamicLoader)
set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testDynamicLoader.cxx)
+2 -1
View File
@@ -1839,7 +1839,8 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
/* Make sure the child is in the process group before we proceed. This
avoids race conditions with calls to the kill function that we make for
signalling process groups. */
while ((readRes = read(pgidPipe[0], &tmp, 1)) > 0) {
while ((readRes = read(pgidPipe[0], &tmp, 1)) > 0 ||
(readRes < 0 && errno == EINTR)) {
}
if (readRes < 0) {
sigprocmask(SIG_SETMASK, &old_mask, 0);
+195 -26
View File
@@ -278,7 +278,116 @@ static bool call_cpuid(int select, int result[4])
}
#endif
// CPUID leaf 0x16 (processor frequency) reader, kept separate from USE_CPUID
// so GCC/Clang gain this query without the other MSVC-only CPUID routines.
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X64) || \
defined(_M_IX86)
# define KWSYS_CPUID_FREQ 1
#else
# define KWSYS_CPUID_FREQ 0
#endif
#if KWSYS_CPUID_FREQ
# if defined(_MSC_VER)
# include <intrin.h>
# else
# include <cpuid.h>
# endif
namespace {
// Base clock in MHz from CPUID leaf 0x16; 0 if the leaf is unsupported.
float kwsysCpuidBaseFrequencyMHz()
{
unsigned int eax = 0;
# if defined(_MSC_VER)
int regs[4] = { 0, 0, 0, 0 };
__cpuid(regs, 0);
if (static_cast<unsigned int>(regs[0]) < 0x16u) {
return 0.0f;
}
__cpuidex(regs, 0x16, 0);
eax = static_cast<unsigned int>(regs[0]);
# else
// __get_cpuid_count() is missing from older <cpuid.h> (GCC < 7, the Apple
// Clang in the macOS 10.10/10.11 SDK), so gate on the max leaf ourselves and
// read via the long-standing __cpuid_count() macro.
if (static_cast<unsigned int>(__get_cpuid_max(0, nullptr)) < 0x16u) {
return 0.0f;
}
unsigned int ebx = 0;
unsigned int ecx = 0;
unsigned int edx = 0;
__cpuid_count(0x16u, 0u, eax, ebx, ecx, edx);
# endif
return static_cast<float>(eax & 0xffffu); // EAX[15:0] = base MHz
}
}
#endif
namespace KWSYS_NAMESPACE {
// Linux cpufreq sysfs resolution, at namespace scope so the test suite can
// drive it with an injected sysfs tree.
namespace SystemInformationDetail {
// First base-10 unsigned integer in a file; false if missing or non-numeric.
static bool ReadFileUInt(std::string const& path, unsigned long long& value)
{
FILE* f = fopen(path.c_str(), "r");
if (!f) {
return false;
}
char buf[64];
size_t n = fread(buf, 1, sizeof(buf) - 1, f);
fclose(f);
buf[n] = '\0';
char* end = nullptr;
unsigned long long const v = strtoull(buf, &end, 10);
if (end == buf) {
return false;
}
value = v;
return true;
}
// Lowest online CPU; 0 (cpu0) when the online file is absent.
static int RepresentativeCpu(std::string const& cpuRoot)
{
unsigned long long v = 0;
if (ReadFileUInt(cpuRoot + "/online", v)) {
return static_cast<int>(v);
}
return 0;
}
// Base/nominal frequency in MHz from cpufreq, or 0 if unavailable.
float LinuxBaseFrequencyMHz(std::string const& cpuRoot)
{
std::string const cpu =
cpuRoot + "/cpu" + std::to_string(RepresentativeCpu(cpuRoot));
unsigned long long value = 0;
// Intel P-state base frequency (kHz).
if (ReadFileUInt(cpu + "/cpufreq/base_frequency", value) && value > 0) {
return static_cast<float>(value) / 1000.0f;
}
// AMD CPPC nominal frequency (MHz).
if (ReadFileUInt(cpu + "/acpi_cppc/nominal_freq", value) && value > 0) {
return static_cast<float>(value);
}
return 0.0f;
}
// Maximum frequency in MHz from cpufreq, or 0 if unavailable. Used as a
// fallback where no base frequency exists.
float LinuxMaxFrequencyMHz(std::string const& cpuRoot)
{
std::string const cpu =
cpuRoot + "/cpu" + std::to_string(RepresentativeCpu(cpuRoot));
unsigned long long value = 0;
if (ReadFileUInt(cpu + "/cpufreq/cpuinfo_max_freq", value) && value > 0) {
return static_cast<float>(value) / 1000.0f;
}
return 0.0f;
}
}
template <typename T>
T min(T a, T b)
{
@@ -2584,6 +2693,14 @@ typedef struct _PROCESSOR_POWER_INFORMATION
ULONG MaxIdleState;
ULONG CurrentIdleState;
} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
# ifndef ALL_PROCESSOR_GROUPS
# define ALL_PROCESSOR_GROUPS 0xffff
# endif
# ifndef STATUS_BUFFER_TOO_SMALL
# define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
# endif
#endif
/** */
@@ -2592,12 +2709,48 @@ bool SystemInformationImplementation::RetrieveCPUClockSpeed()
bool retrieved = false;
#if defined(_WIN32)
PROCESSOR_POWER_INFORMATION powerInfo[64];
NTSTATUS status =
CallNtPowerInformation(ProcessorInformation, nullptr, 0, powerInfo,
sizeof(PROCESSOR_POWER_INFORMATION) * 64);
# if KWSYS_CPUID_FREQ
// Prefer CPUID leaf 0x16 base frequency; fall back to the OS max clock.
{
float const baseMHz = kwsysCpuidBaseFrequencyMHz();
if (baseMHz > 0.0f) {
this->CPUSpeedInMHz = baseMHz;
return true;
}
}
# endif
// CallNtPowerInformation writes one record per logical processor; a
// fixed 64-entry buffer overflows across multiple processor groups.
using GetMaximumProcessorCountType = DWORD(WINAPI*)(WORD);
static GetMaximumProcessorCountType pGetMaximumProcessorCount =
reinterpret_cast<GetMaximumProcessorCountType>((void*)GetProcAddress(
GetModuleHandleW(L"kernel32"), "GetMaximumProcessorCount"));
if (status == 0) {
// GetMaximumProcessorCount() needs Windows 7+; older systems cap at 64.
DWORD processorCount = 64;
if (pGetMaximumProcessorCount) {
DWORD const maxProcessorCount =
pGetMaximumProcessorCount(ALL_PROCESSOR_GROUPS);
if (maxProcessorCount > 0) {
processorCount = maxProcessorCount;
}
}
std::vector<PROCESSOR_POWER_INFORMATION> powerInfo(processorCount);
NTSTATUS status = STATUS_BUFFER_TOO_SMALL;
// Grow and retry if the estimate was too small (e.g. CPU hot-add).
for (int attempt = 0; attempt < 4 && status == STATUS_BUFFER_TOO_SMALL;
++attempt) {
status = CallNtPowerInformation(
ProcessorInformation, nullptr, 0, powerInfo.data(),
static_cast<ULONG>(sizeof(PROCESSOR_POWER_INFORMATION) *
powerInfo.size()));
if (status == STATUS_BUFFER_TOO_SMALL) {
powerInfo.resize(powerInfo.size() * 2);
}
}
if (status == 0 && !powerInfo.empty()) {
this->CPUSpeedInMHz = (float)powerInfo[0].MaxMhz;
retrieved = true;
}
@@ -3587,29 +3740,45 @@ bool SystemInformationImplementation::RetrieveInformationFromCpuInfoFile()
this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical =
this->NumberOfLogicalCPU / this->NumberOfPhysicalCPU;
// CPU speed (checking only the first processor)
std::string CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "cpu MHz");
if (!CPUSpeed.empty()) {
this->CPUSpeedInMHz = static_cast<float>(atof(CPUSpeed.c_str()));
}
#ifdef __linux
else {
// Linux Sparc: CPU speed is in Hz and encoded in hexadecimal
CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "Cpu0ClkTck");
if (!CPUSpeed.empty()) {
this->CPUSpeedInMHz =
static_cast<float>(strtoull(CPUSpeed.c_str(), nullptr, 16)) /
1000000.0f;
} else {
// if the kernel is build as Sparc32 it's in decimal, note the different
// case
CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "CPU0ClkTck");
this->CPUSpeedInMHz =
static_cast<float>(strtoull(CPUSpeed.c_str(), nullptr, 10)) /
1000000.0f;
}
// Prefer a stable base frequency over the fluctuating "cpu MHz" current.
char const* const cpuRoot = "/sys/devices/system/cpu";
float baseMHz = SystemInformationDetail::LinuxBaseFrequencyMHz(cpuRoot);
#if KWSYS_CPUID_FREQ
if (baseMHz == 0.0f) {
baseMHz = kwsysCpuidBaseFrequencyMHz();
}
#endif
if (baseMHz == 0.0f) {
baseMHz = SystemInformationDetail::LinuxMaxFrequencyMHz(cpuRoot);
}
if (baseMHz > 0.0f) {
this->CPUSpeedInMHz = baseMHz;
} else {
// Last resort: current frequency, so we do not regress to no value.
std::string CPUSpeed =
this->ExtractValueFromCpuInfoFile(buffer, "cpu MHz");
if (!CPUSpeed.empty()) {
this->CPUSpeedInMHz = static_cast<float>(atof(CPUSpeed.c_str()));
}
#ifdef __linux
else {
// Linux Sparc: CPU speed is in Hz and encoded in hexadecimal
CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "Cpu0ClkTck");
if (!CPUSpeed.empty()) {
this->CPUSpeedInMHz =
static_cast<float>(strtoull(CPUSpeed.c_str(), nullptr, 16)) /
1000000.0f;
} else {
// if the kernel is build as Sparc32 it's in decimal, note the
// different case
CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "CPU0ClkTck");
this->CPUSpeedInMHz =
static_cast<float>(strtoull(CPUSpeed.c_str(), nullptr, 10)) /
1000000.0f;
}
}
#endif
}
// Chip family
std::string familyStr =
+5
View File
@@ -1,5 +1,10 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__OpenBSD__)
/* NOLINTNEXTLINE(bugprone-reserved-identifier) */
# define _XOPEN_SOURCE 600
#endif
#include "kwsysPrivate.h"
#include KWSYS_HEADER(String.h)
@@ -0,0 +1,91 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
#include "kwsysPrivate.h"
#include KWSYS_HEADER(SystemTools.hxx)
// Work-around CMake dependency scanning limitation. This must
// duplicate the above list of headers.
#if 0
# include "SystemTools.hxx.in"
#endif
#include <cmath>
#include <fstream>
#include <iostream>
#include <string>
// Internal resolvers under test, driven with an injected sysfs tree.
namespace kwsys {
namespace SystemInformationDetail {
float LinuxBaseFrequencyMHz(std::string const& cpuRoot);
float LinuxMaxFrequencyMHz(std::string const& cpuRoot);
}
}
namespace {
int failures = 0;
void writeFile(std::string const& path, std::string const& content)
{
std::ofstream f(path.c_str());
f << content;
}
void expect(char const* name, float actual, float expected)
{
if (std::fabs(actual - expected) > 0.01f) {
std::cerr << "FAIL: " << name << ": expected " << expected << " got "
<< actual << "\n";
++failures;
}
}
}
int testSystemInformationBaseFrequency(int, char*[])
{
using kwsys::SystemInformationDetail::LinuxBaseFrequencyMHz;
using kwsys::SystemInformationDetail::LinuxMaxFrequencyMHz;
using kwsys::SystemTools;
std::string const root = "testSystemInformationBaseFrequency.dir";
// Intel P-state base_frequency (kHz) -> MHz.
SystemTools::RemoveADirectory(root);
SystemTools::MakeDirectory(root + "/cpu0/cpufreq");
writeFile(root + "/online", "0-15\n");
writeFile(root + "/cpu0/cpufreq/base_frequency", "2200000\n");
expect("base_frequency", LinuxBaseFrequencyMHz(root), 2200.0f);
// cpuinfo_max_freq (kHz) -> MHz.
writeFile(root + "/cpu0/cpufreq/cpuinfo_max_freq", "5000000\n");
expect("cpuinfo_max_freq", LinuxMaxFrequencyMHz(root), 5000.0f);
// Representative CPU follows the lowest online id.
SystemTools::RemoveADirectory(root);
SystemTools::MakeDirectory(root + "/cpu2/cpufreq");
writeFile(root + "/online", "2-3,8\n");
writeFile(root + "/cpu2/cpufreq/base_frequency", "2400000\n");
expect("representative-cpu", LinuxBaseFrequencyMHz(root), 2400.0f);
// AMD CPPC nominal_freq (already MHz) when base_frequency is absent.
SystemTools::RemoveADirectory(root);
SystemTools::MakeDirectory(root + "/cpu0/acpi_cppc");
writeFile(root + "/cpu0/acpi_cppc/nominal_freq", "3000\n");
expect("nominal_freq", LinuxBaseFrequencyMHz(root), 3000.0f);
// Malformed and zero values are rejected (treated as unavailable).
SystemTools::RemoveADirectory(root);
SystemTools::MakeDirectory(root + "/cpu0/cpufreq");
writeFile(root + "/cpu0/cpufreq/base_frequency", "not-a-number\n");
expect("malformed", LinuxBaseFrequencyMHz(root), 0.0f);
writeFile(root + "/cpu0/cpufreq/base_frequency", "0\n");
expect("zero", LinuxBaseFrequencyMHz(root), 0.0f);
// An absent tree yields no value.
SystemTools::RemoveADirectory(root);
expect("absent-base", LinuxBaseFrequencyMHz(root), 0.0f);
expect("absent-max", LinuxMaxFrequencyMHz(root), 0.0f);
SystemTools::RemoveADirectory(root);
return failures == 0 ? 0 : 1;
}