mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Merge branch 'upstream-KWSys' into update-kwsys
# By KWSys Upstream * upstream-KWSys: KWSys 2026-09-03 (151e9c02)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user