Remove CPU telemetry (background temperature/clock speed sampling)
The background thread that sampled CPU temperature and clock speed during a run relied on undocumented, OS-specific interfaces: raw AppleSMC keys on macOS, WMI ACPI thermal zones on Windows, and /sys/class/thermal or /sys/class/hwmon on Linux. Testing on Apple Silicon showed the SMC key scan can silently switch between different physical sensors mid-run, producing discontinuous jumps in reported temperature, and live per-core clock speed was never obtainable on macOS through any public API. Disable the feature entirely rather than ship unreliable data: drop the telemetry monitor thread, its platform-specific sensor backends, and the telemetry field from upload payloads. The pre-run background system-activity check (--no-system-check) is unrelated and untouched. Bump FB_VERSION to 0.4.2.
This commit is contained in:
@@ -175,5 +175,5 @@ jobs:
|
||||
release/SHA256SUMS \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--title "Release $RELEASE_TAG" \
|
||||
--notes "Hotfix: CPU telemetry (temperature and clock speed) recorded during benchmark runs was not working on Windows or macOS, showing empty temperature data points and a constant clock speed. Windows temperature now reads the ACPI thermal zone over WMI, and clock speed reads live per-core frequency via CallNtPowerInformation instead of a static registry value. macOS temperature now reads the SMC directly across Intel and Apple Silicon (M1-M5) Macs. macOS clock speed now reports as unavailable rather than the fixed nominal frequency it previously showed, since no live-frequency API exists on macOS." \
|
||||
--notes "Removed CPU telemetry (background temperature and clock speed sampling during benchmark runs). It relied on undocumented, OS-specific interfaces (raw AppleSMC keys on macOS, WMI ACPI thermal zones on Windows, /sys/class/thermal and /sys/class/hwmon on Linux) that proved unstable and inconsistent across operating systems and hardware, and live per-core clock speed was never obtainable on macOS at all through any public API. Rather than ship telemetry data that can silently jump between physical sensors mid-run or simply not exist on a given platform, this release disables the feature entirely: fossbench.net no longer accepts or displays it, and the client no longer collects it. The one-time background system-activity check (CPU/memory/process sampling before a run) is unaffected and still available via --no-system-check." \
|
||||
--generate-notes
|
||||
|
||||
@@ -178,21 +178,20 @@ $(DIST)/fossbench-linux-ppc64le: $(DRIVER) $(DRIVER_DEPS) $(ASM_PPC64LE) | $(DIS
|
||||
@echo "built $@"
|
||||
|
||||
$(DIST)/fossbench-macos-arm64: $(DRIVER) $(DRIVER_DEPS) $(ASM_ARM64) | $(DIST)
|
||||
$(CC_MACOS_ARM64) -arch arm64 $(CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS) -framework IOKit -framework CoreFoundation
|
||||
$(CC_MACOS_ARM64) -arch arm64 $(CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS)
|
||||
@echo "built $@"
|
||||
|
||||
$(DIST)/fossbench-macos-amd64: $(DRIVER) $(DRIVER_DEPS) $(ASM_AMD64) | $(DIST)
|
||||
MACOSX_DEPLOYMENT_TARGET=$(MACOS_AMD64_MIN) $(CC_MACOS_AMD64) -arch x86_64 -mmacosx-version-min=$(MACOS_AMD64_MIN) $(CFLAGS) $(PTHREAD) $(LDFLAGS) -Wl,-no_fixup_chains -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS) -framework IOKit -framework CoreFoundation
|
||||
MACOSX_DEPLOYMENT_TARGET=$(MACOS_AMD64_MIN) $(CC_MACOS_AMD64) -arch x86_64 -mmacosx-version-min=$(MACOS_AMD64_MIN) $(CFLAGS) $(PTHREAD) $(LDFLAGS) -Wl,-no_fixup_chains -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS)
|
||||
@echo "built $@"
|
||||
|
||||
# Windows builds are static and use WinHTTP. Temperature/clock telemetry
|
||||
# additionally needs WMI (ole32/oleaut32/wbemuuid) and PowrProf.
|
||||
# Windows builds are static and use WinHTTP.
|
||||
$(DIST)/fossbench-windows-amd64.exe: $(DRIVER) $(DRIVER_DEPS) $(ASM_AMD64) | $(DIST)
|
||||
$(CC_WINDOWS_AMD64) $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_AMD64) -lm -lwinhttp -ladvapi32 -lole32 -loleaut32 -lwbemuuid -lpowrprof
|
||||
$(CC_WINDOWS_AMD64) $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_AMD64) -lm -lwinhttp -ladvapi32
|
||||
@echo "built $@"
|
||||
|
||||
$(DIST)/fossbench-windows-i386.exe: $(DRIVER) $(DRIVER_DEPS) $(SRC_I386) | $(DIST)
|
||||
$(CC_WINDOWS_I386) -march=i386 $(WINDOWS_I386_XP_CFLAGS) $(CFLAGS) -static $(WINDOWS_I386_XP_LDFLAGS) -o $@ $(DRIVER) $(SRC_I386) -lm -lwinhttp -ladvapi32 -lole32 -loleaut32 -lwbemuuid -lpowrprof
|
||||
$(CC_WINDOWS_I386) -march=i386 $(WINDOWS_I386_XP_CFLAGS) $(CFLAGS) -static $(WINDOWS_I386_XP_LDFLAGS) -o $@ $(DRIVER) $(SRC_I386) -lm -lwinhttp -ladvapi32
|
||||
@echo "built $@"
|
||||
|
||||
# Add a native rule if one was not already made above.
|
||||
|
||||
+2
-457
@@ -12,20 +12,6 @@
|
||||
# include <tlhelp32.h>
|
||||
# include <winhttp.h>
|
||||
# include <process.h>
|
||||
# include <powrprof.h>
|
||||
# include <wbemidl.h>
|
||||
# include <oleauto.h>
|
||||
/* Documented by Microsoft but not declared in MinGW's powrprof.h; including
|
||||
* <ddk/ntpoapi.h> for it collides with definitions already pulled in by
|
||||
* <windows.h>, so declare the same layout locally instead. */
|
||||
typedef struct _PROCESSOR_POWER_INFORMATION {
|
||||
ULONG Number;
|
||||
ULONG MaxMhz;
|
||||
ULONG CurrentMhz;
|
||||
ULONG MhzLimit;
|
||||
ULONG MaxIdleState;
|
||||
ULONG CurrentIdleState;
|
||||
} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
|
||||
#else
|
||||
# include <pthread.h>
|
||||
# include <unistd.h>
|
||||
@@ -46,14 +32,13 @@ typedef struct _PROCESSOR_POWER_INFORMATION {
|
||||
# include <sys/sysctl.h>
|
||||
# include <mach/mach_time.h>
|
||||
# include <mach/mach.h>
|
||||
# include <IOKit/IOKitLib.h>
|
||||
#endif
|
||||
|
||||
/* The server URL can be changed when building. */
|
||||
#ifndef FB_API_BASE_URL
|
||||
# define FB_API_BASE_URL "http://fossbench.net"
|
||||
#endif
|
||||
#define FB_VERSION "0.4.0"
|
||||
#define FB_VERSION "0.4.2"
|
||||
|
||||
/*
|
||||
* Native Integer Math and Memory Bandwidth are the only two *measured*
|
||||
@@ -188,8 +173,6 @@ extern uint64_t fb_c_chase(void **ptrs, uint64_t steps);
|
||||
# define REPEATS 3 /* Number of tries. */
|
||||
#endif
|
||||
|
||||
#define TELEMETRY_REFRESH_INTERVAL 0.5
|
||||
|
||||
/* Simple repeatable random numbers. */
|
||||
|
||||
static uint64_t rng_state = 0x853c49e6748fea9bULL;
|
||||
@@ -237,439 +220,6 @@ struct background_metrics {
|
||||
int samples, available;
|
||||
};
|
||||
|
||||
|
||||
#define MAX_TELEMETRY_SAMPLES 1800
|
||||
|
||||
struct telemetry_sample {
|
||||
uint64_t elapsed_ms;
|
||||
double temperature_c, clock_mhz;
|
||||
};
|
||||
|
||||
struct telemetry_monitor {
|
||||
struct telemetry_sample samples[MAX_TELEMETRY_SAMPLES];
|
||||
size_t count;
|
||||
double started;
|
||||
volatile int stop;
|
||||
#if defined(_WIN32)
|
||||
HANDLE thread;
|
||||
#else
|
||||
pthread_t thread;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int read_number_file(const char *path, double *value)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
int ok;
|
||||
if (!f) return 0;
|
||||
ok = fscanf(f, "%lf", value) == 1;
|
||||
fclose(f);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
static int read_text_file(const char *path, char *value, size_t cap)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f || !fgets(value, (int)cap, f)) { if (f) fclose(f); return 0; }
|
||||
fclose(f);
|
||||
value[strcspn(value, "\r\n")] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cpu_sensor_name(const char *name)
|
||||
{
|
||||
return strstr(name, "cpu") || strstr(name, "CPU") || strstr(name, "package") ||
|
||||
strstr(name, "Package") || strstr(name, "coretemp") || strstr(name, "k10temp") ||
|
||||
strstr(name, "zenpower") || strstr(name, "x86_pkg_temp") || strstr(name, "soc_thermal");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* Reads MSAcpi_ThermalZoneTemperature from the ACPI thermal zone WMI namespace.
|
||||
* Only populated on hardware whose firmware exposes it; many laptops do not. */
|
||||
static IWbemServices *wmi_connect_thermal_namespace(void)
|
||||
{
|
||||
IWbemLocator *locator = NULL;
|
||||
IWbemServices *services = NULL;
|
||||
BSTR namespace_path = NULL;
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *)&locator);
|
||||
if (FAILED(hr) || !locator) return NULL;
|
||||
|
||||
namespace_path = SysAllocString(L"ROOT\\WMI");
|
||||
if (!namespace_path) { locator->lpVtbl->Release(locator); return NULL; }
|
||||
hr = locator->lpVtbl->ConnectServer(locator, namespace_path, NULL, NULL, NULL, 0, NULL, NULL, &services);
|
||||
SysFreeString(namespace_path);
|
||||
locator->lpVtbl->Release(locator);
|
||||
if (FAILED(hr) || !services) return NULL;
|
||||
|
||||
hr = CoSetProxyBlanket((IUnknown *)services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
|
||||
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
|
||||
if (FAILED(hr)) { services->lpVtbl->Release(services); return NULL; }
|
||||
return services;
|
||||
}
|
||||
|
||||
static double wmi_query_temperature(IWbemServices *services)
|
||||
{
|
||||
IEnumWbemClassObject *enumerator = NULL;
|
||||
BSTR language = NULL, query = NULL;
|
||||
HRESULT hr;
|
||||
double hottest = -1.0;
|
||||
|
||||
language = SysAllocString(L"WQL");
|
||||
query = SysAllocString(L"SELECT CurrentTemperature FROM MSAcpi_ThermalZoneTemperature");
|
||||
if (!language || !query) goto done;
|
||||
|
||||
hr = services->lpVtbl->ExecQuery(services, language, query,
|
||||
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &enumerator);
|
||||
if (FAILED(hr) || !enumerator) goto done;
|
||||
|
||||
for (;;) {
|
||||
IWbemClassObject *obj = NULL;
|
||||
ULONG returned = 0;
|
||||
VARIANT value;
|
||||
hr = enumerator->lpVtbl->Next(enumerator, WBEM_INFINITE, 1, &obj, &returned);
|
||||
if (FAILED(hr) || returned == 0) break;
|
||||
VariantInit(&value);
|
||||
/* CurrentTemperature is reported in tenths of a Kelvin. */
|
||||
if (SUCCEEDED(obj->lpVtbl->Get(obj, L"CurrentTemperature", 0, &value, NULL, NULL))) {
|
||||
double tenths_kelvin = 0.0;
|
||||
if (value.vt == VT_I4) tenths_kelvin = (double)value.lVal;
|
||||
else if (value.vt == VT_UI4) tenths_kelvin = (double)value.ulVal;
|
||||
if (tenths_kelvin > 0.0) {
|
||||
double celsius = tenths_kelvin / 10.0 - 273.15;
|
||||
if (celsius > -50.0 && celsius <= 150.0 && celsius > hottest) hottest = celsius;
|
||||
}
|
||||
}
|
||||
VariantClear(&value);
|
||||
obj->lpVtbl->Release(obj);
|
||||
}
|
||||
enumerator->lpVtbl->Release(enumerator);
|
||||
done:
|
||||
if (query) SysFreeString(query);
|
||||
if (language) SysFreeString(language);
|
||||
return hottest;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* Talks to the AppleSMC user client directly; there is no public framework
|
||||
* for this. Struct layout and command bytes come from the SMC protocol that
|
||||
* has been stable across Intel and Apple Silicon Macs for over a decade. */
|
||||
typedef struct {
|
||||
char major, minor, build, reserved_;
|
||||
uint16_t release;
|
||||
} fb_smc_vers_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t version, length;
|
||||
uint32_t cpu_p_limit, gpu_p_limit, mem_p_limit;
|
||||
} fb_smc_plimit_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t data_size;
|
||||
uint32_t data_type;
|
||||
char data_attributes;
|
||||
} fb_smc_key_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t key;
|
||||
fb_smc_vers_t vers;
|
||||
fb_smc_plimit_t plimit;
|
||||
fb_smc_key_info_t key_info;
|
||||
char result, status, data8;
|
||||
uint32_t data32;
|
||||
unsigned char bytes[32];
|
||||
} fb_smc_data_t;
|
||||
|
||||
#define FB_SMC_KERNEL_INDEX 2
|
||||
#define FB_SMC_CMD_READ_BYTES 5
|
||||
#define FB_SMC_CMD_READ_KEY_INFO 9
|
||||
|
||||
static uint32_t smc_key_from_string(const char *s)
|
||||
{
|
||||
return ((uint32_t)(unsigned char)s[0] << 24) | ((uint32_t)(unsigned char)s[1] << 16) |
|
||||
((uint32_t)(unsigned char)s[2] << 8) | (uint32_t)(unsigned char)s[3];
|
||||
}
|
||||
|
||||
static kern_return_t smc_call(io_connect_t conn, fb_smc_data_t *in, fb_smc_data_t *out)
|
||||
{
|
||||
size_t out_size = sizeof(*out);
|
||||
return IOConnectCallStructMethod(conn, FB_SMC_KERNEL_INDEX, in, sizeof(*in), out, &out_size);
|
||||
}
|
||||
|
||||
static int smc_read_temperature(io_connect_t conn, const char *key, double *out)
|
||||
{
|
||||
fb_smc_data_t in, info_out, read_out;
|
||||
|
||||
memset(&in, 0, sizeof(in));
|
||||
memset(&info_out, 0, sizeof(info_out));
|
||||
in.key = smc_key_from_string(key);
|
||||
in.data8 = FB_SMC_CMD_READ_KEY_INFO;
|
||||
if (smc_call(conn, &in, &info_out) != KERN_SUCCESS || info_out.key_info.data_size == 0)
|
||||
return 0;
|
||||
|
||||
memset(&in, 0, sizeof(in));
|
||||
memset(&read_out, 0, sizeof(read_out));
|
||||
in.key = smc_key_from_string(key);
|
||||
in.key_info.data_size = info_out.key_info.data_size;
|
||||
in.data8 = FB_SMC_CMD_READ_BYTES;
|
||||
if (smc_call(conn, &in, &read_out) != KERN_SUCCESS)
|
||||
return 0;
|
||||
|
||||
if (info_out.key_info.data_type == smc_key_from_string("sp78") && info_out.key_info.data_size >= 2) {
|
||||
int16_t raw = (int16_t)((read_out.bytes[0] << 8) | read_out.bytes[1]);
|
||||
*out = raw / 256.0;
|
||||
return 1;
|
||||
}
|
||||
if (info_out.key_info.data_type == smc_key_from_string("flt ") && info_out.key_info.data_size >= 4) {
|
||||
float f;
|
||||
memcpy(&f, read_out.bytes, sizeof(f));
|
||||
*out = (double)f;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static io_connect_t smc_open(void)
|
||||
{
|
||||
io_service_t service;
|
||||
io_connect_t conn = IO_OBJECT_NULL;
|
||||
|
||||
service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC"));
|
||||
if (service == IO_OBJECT_NULL) return IO_OBJECT_NULL;
|
||||
if (IOServiceOpen(service, mach_task_self(), 0, &conn) != KERN_SUCCESS) conn = IO_OBJECT_NULL;
|
||||
IOObjectRelease(service);
|
||||
return conn;
|
||||
}
|
||||
|
||||
/* CPU-related SMC keys across Intel Macs and Apple Silicon M1-M5, gathered
|
||||
* from publicly reverse-engineered sensor tables (e.g. exelban/stats,
|
||||
* acidanthera/VirtualSMC). Coverage varies by exact model/chip revision. */
|
||||
static const char *const smc_cpu_temp_keys[] = {
|
||||
"TC0D", "TC0E", "TC0F", "TC0P",
|
||||
"Tp00", "Tp01", "Tp04", "Tp05", "Tp08", "Tp09", "Tp0C", "Tp0D", "Tp0f", "Tp0G",
|
||||
"Tp0H", "Tp0j", "Tp0K", "Tp0L", "Tp0m", "Tp0n", "Tp0O", "Tp0P", "Tp0p", "Tp0r",
|
||||
"Tp0R", "Tp0T", "Tp0u", "Tp0U", "Tp0X", "Tp0a", "Tp0b", "Tp0d", "Tp0g", "Tp0y",
|
||||
"Tp1h", "Tp1l", "Tp1p", "Tp1t",
|
||||
"Tc0a", "Tc0b", "Tc0x", "Tc0z",
|
||||
};
|
||||
#endif
|
||||
|
||||
static double sample_cpu_temperature(void)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
DIR *dir = opendir("/sys/class/thermal");
|
||||
struct dirent *entry;
|
||||
double hottest = -1.0;
|
||||
if (!dir) return -1.0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
char path[512], type[128];
|
||||
double value;
|
||||
if (strncmp(entry->d_name, "thermal_zone", 12) != 0) continue;
|
||||
snprintf(path, sizeof(path), "/sys/class/thermal/%s/type", entry->d_name);
|
||||
if (!read_text_file(path, type, sizeof(type)) || !cpu_sensor_name(type)) continue;
|
||||
snprintf(path, sizeof(path), "/sys/class/thermal/%s/temp", entry->d_name);
|
||||
if (read_number_file(path, &value)) {
|
||||
if (value > 1000.0) value /= 1000.0;
|
||||
if (value >= 0.0 && value <= 150.0 && value > hottest) hottest = value;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
if (hottest < 0.0) {
|
||||
dir = opendir("/sys/class/hwmon");
|
||||
if (!dir) return -1.0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
char base[512], path[512], name[128];
|
||||
DIR *sensor_dir;
|
||||
struct dirent *sensor;
|
||||
if (strncmp(entry->d_name, "hwmon", 5) != 0) continue;
|
||||
snprintf(base, sizeof(base), "/sys/class/hwmon/%s", entry->d_name);
|
||||
snprintf(path, sizeof(path), "%s/name", base);
|
||||
if (!read_text_file(path, name, sizeof(name)) || !cpu_sensor_name(name)) continue;
|
||||
sensor_dir = opendir(base);
|
||||
if (!sensor_dir) continue;
|
||||
while ((sensor = readdir(sensor_dir)) != NULL) {
|
||||
size_t length = strlen(sensor->d_name);
|
||||
double value;
|
||||
if (strncmp(sensor->d_name, "temp", 4) != 0 || length < 7 || strcmp(sensor->d_name + length - 6, "_input") != 0) continue;
|
||||
snprintf(path, sizeof(path), "%s/%s", base, sensor->d_name);
|
||||
if (read_number_file(path, &value)) {
|
||||
if (value > 1000.0) value /= 1000.0;
|
||||
if (value >= 0.0 && value <= 150.0 && value > hottest) hottest = value;
|
||||
}
|
||||
}
|
||||
closedir(sensor_dir);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
return hottest;
|
||||
#elif defined(_WIN32)
|
||||
{
|
||||
static int initialized = 0;
|
||||
static IWbemServices *services = NULL;
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
services = wmi_connect_thermal_namespace();
|
||||
}
|
||||
return services ? wmi_query_temperature(services) : -1.0;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
{
|
||||
static int initialized = 0;
|
||||
static io_connect_t conn = IO_OBJECT_NULL;
|
||||
double hottest = -1.0;
|
||||
size_t i;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
conn = smc_open();
|
||||
}
|
||||
if (conn == IO_OBJECT_NULL) return -1.0;
|
||||
|
||||
for (i = 0; i < sizeof(smc_cpu_temp_keys) / sizeof(smc_cpu_temp_keys[0]); i++) {
|
||||
double value;
|
||||
if (smc_read_temperature(conn, smc_cpu_temp_keys[i], &value) &&
|
||||
value > 0.0 && value <= 150.0 && value > hottest)
|
||||
hottest = value;
|
||||
}
|
||||
return hottest;
|
||||
}
|
||||
#else
|
||||
return -1.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static double sample_cpu_clock_mhz(void)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
DIR *dir = opendir("/sys/devices/system/cpu");
|
||||
struct dirent *entry;
|
||||
double total = 0.0;
|
||||
long count = 0;
|
||||
if (dir) {
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
char path[512], *end;
|
||||
double value;
|
||||
if (strncmp(entry->d_name, "cpu", 3) != 0 || !isdigit((unsigned char)entry->d_name[3])) continue;
|
||||
strtol(entry->d_name + 3, &end, 10);
|
||||
if (*end) continue;
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/%s/cpufreq/scaling_cur_freq", entry->d_name);
|
||||
if (read_number_file(path, &value) && value > 0.0) { total += value / 1000.0; count++; }
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
if (count) return total / (double)count;
|
||||
{
|
||||
FILE *f = fopen("/proc/cpuinfo", "r");
|
||||
char line[256];
|
||||
if (!f) return -1.0;
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
double value;
|
||||
if (sscanf(line, "cpu MHz%*[^:]: %lf", &value) == 1 && value > 0.0) { total += value; count++; }
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
return count ? total / (double)count : -1.0;
|
||||
#elif defined(__APPLE__)
|
||||
/* No public API reports live per-core frequency on macOS (none exists at
|
||||
* all on Apple Silicon); sysctl hw.cpufrequency is a fixed nominal value,
|
||||
* not a real-time reading, so report unavailable rather than a constant. */
|
||||
return -1.0;
|
||||
#elif defined(_WIN32)
|
||||
{
|
||||
PROCESSOR_POWER_INFORMATION info[64];
|
||||
SYSTEM_INFO sysinfo;
|
||||
DWORD count, i;
|
||||
double total = 0.0;
|
||||
|
||||
GetSystemInfo(&sysinfo);
|
||||
count = sysinfo.dwNumberOfProcessors;
|
||||
if (count == 0) count = 1;
|
||||
if (count > 64) count = 64;
|
||||
if (CallNtPowerInformation(ProcessorInformation, NULL, 0, info,
|
||||
count * sizeof(PROCESSOR_POWER_INFORMATION)) != 0)
|
||||
return -1.0;
|
||||
for (i = 0; i < count; i++) total += info[i].CurrentMhz;
|
||||
return count ? total / (double)count : -1.0;
|
||||
}
|
||||
#else
|
||||
return -1.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void record_telemetry_sample(struct telemetry_monitor *monitor)
|
||||
{
|
||||
struct telemetry_sample *sample;
|
||||
if (monitor->count >= MAX_TELEMETRY_SAMPLES) return;
|
||||
sample = &monitor->samples[monitor->count++];
|
||||
sample->elapsed_ms = (uint64_t)((now_seconds() - monitor->started) * 1000.0);
|
||||
sample->temperature_c = sample_cpu_temperature();
|
||||
sample->clock_mhz = sample_cpu_clock_mhz();
|
||||
}
|
||||
|
||||
static void telemetry_sleep(void)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
Sleep(25);
|
||||
#else
|
||||
usleep(25000);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
static DWORD WINAPI telemetry_entry(LPVOID arg)
|
||||
#else
|
||||
static void *telemetry_entry(void *arg)
|
||||
#endif
|
||||
{
|
||||
struct telemetry_monitor *monitor = (struct telemetry_monitor *)arg;
|
||||
double next = monitor->started;
|
||||
#if defined(_WIN32)
|
||||
/* sample_cpu_temperature() lazily opens a WMI connection on first use,
|
||||
* which requires this thread's COM apartment to be initialized first. */
|
||||
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
#endif
|
||||
while (!monitor->stop) {
|
||||
double current = now_seconds();
|
||||
if (current >= next) { record_telemetry_sample(monitor); next += TELEMETRY_REFRESH_INTERVAL; }
|
||||
telemetry_sleep();
|
||||
}
|
||||
record_telemetry_sample(monitor);
|
||||
#if defined(_WIN32)
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int start_telemetry_monitor(struct telemetry_monitor *monitor, double started)
|
||||
{
|
||||
memset(monitor, 0, sizeof(*monitor));
|
||||
monitor->started = started;
|
||||
#if defined(_WIN32)
|
||||
monitor->thread = CreateThread(NULL, 0, telemetry_entry, monitor, 0, NULL);
|
||||
return monitor->thread != NULL;
|
||||
#else
|
||||
return pthread_create(&monitor->thread, NULL, telemetry_entry, monitor) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void stop_telemetry_monitor(struct telemetry_monitor *monitor, int started)
|
||||
{
|
||||
if (!started) return;
|
||||
monitor->stop = 1;
|
||||
#if defined(_WIN32)
|
||||
WaitForSingleObject(monitor->thread, INFINITE);
|
||||
CloseHandle(monitor->thread);
|
||||
#else
|
||||
pthread_join(monitor->thread, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct cpu_snapshot { uint64_t total, idle; };
|
||||
|
||||
static int take_cpu_snapshot(struct cpu_snapshot *s)
|
||||
@@ -1239,9 +789,7 @@ int fossbench_run(int verbose, int upload_mode, int system_check)
|
||||
struct result real_multi[NTESTS], real_single[NTESTS];
|
||||
struct system_info system_info;
|
||||
struct background_metrics background;
|
||||
struct telemetry_monitor telemetry;
|
||||
double benchmark_started;
|
||||
int telemetry_started;
|
||||
uint64_t duration_ms;
|
||||
size_t i;
|
||||
|
||||
@@ -1262,7 +810,6 @@ int fossbench_run(int verbose, int upload_mode, int system_check)
|
||||
}
|
||||
}
|
||||
benchmark_started = now_seconds();
|
||||
telemetry_started = start_telemetry_monitor(&telemetry, benchmark_started);
|
||||
|
||||
printf("\n preparing workloads...");
|
||||
fflush(stdout);
|
||||
@@ -1313,7 +860,6 @@ int fossbench_run(int verbose, int upload_mode, int system_check)
|
||||
}
|
||||
|
||||
printf(" --------------------------------------------------------------------------\n");
|
||||
stop_telemetry_monitor(&telemetry, telemetry_started);
|
||||
|
||||
duration_ms = (uint64_t)((now_seconds() - benchmark_started) * 1000.0);
|
||||
printf(" %-24s %41.2fs\n", "TOTAL DURATION", (double)duration_ms / 1000.0);
|
||||
@@ -1344,8 +890,7 @@ int fossbench_run(int verbose, int upload_mode, int system_check)
|
||||
fprintf(stderr, " Upload support is disabled in this build.\n");
|
||||
#else
|
||||
upload_results(&system_info, raw_multi, raw_single,
|
||||
real_multi, real_single, duration_ms, &background,
|
||||
&telemetry);
|
||||
real_multi, real_single, duration_ms, &background);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+1
-23
@@ -157,8 +157,7 @@ static int upload_results(const struct system_info *info,
|
||||
const struct result *raw_single,
|
||||
const struct result *real_multi,
|
||||
const struct result *real_single, uint64_t duration_ms,
|
||||
const struct background_metrics *background,
|
||||
const struct telemetry_monitor *telemetry)
|
||||
const struct background_metrics *background)
|
||||
{
|
||||
char host[256], port[16], path[512], payload[131072];
|
||||
char auth_header[600], response_body[2048], claim_url[1024], result_url[1024];
|
||||
@@ -225,27 +224,6 @@ static int upload_results(const struct system_info *info,
|
||||
if (n < 0 || (size_t)n >= sizeof payload - used) return 0;
|
||||
used += (size_t)n;
|
||||
if (!append_result_tests(payload, sizeof payload, &used, real_multi, real_single)) return 0;
|
||||
if (used + 3 >= sizeof payload) return 0;
|
||||
n = snprintf(payload + used, sizeof payload - used, "],\"telemetry\":[");
|
||||
if (n < 0 || (size_t)n >= sizeof payload - used) return 0;
|
||||
used += (size_t)n;
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < telemetry->count; i++) {
|
||||
const struct telemetry_sample *sample = &telemetry->samples[i];
|
||||
char temperature[32], clock[32];
|
||||
if (sample->temperature_c < 0.0) strcpy(temperature, "null");
|
||||
else snprintf(temperature, sizeof(temperature), "%.2f", sample->temperature_c);
|
||||
if (sample->clock_mhz < 0.0) strcpy(clock, "null");
|
||||
else snprintf(clock, sizeof(clock), "%.2f", sample->clock_mhz);
|
||||
n = snprintf(payload + used, sizeof payload - used,
|
||||
"%s{\"elapsed_ms\":%llu,\"temperature_c\":%s,\"clock_mhz\":%s}",
|
||||
i ? "," : "", (unsigned long long)sample->elapsed_ms,
|
||||
temperature, clock);
|
||||
if (n < 0 || (size_t)n >= sizeof payload - used) return 0;
|
||||
used += (size_t)n;
|
||||
}
|
||||
}
|
||||
if (used + 4 >= sizeof payload) return 0;
|
||||
memcpy(payload + used, "]}}", 4);
|
||||
payload_len = (int)(used + 3);
|
||||
|
||||
Reference in New Issue
Block a user