Add pre-benchmark system activity checks

This commit is contained in:
2026-07-18 12:09:26 -05:00
parent cefa9021ec
commit 5f1a2b2cea
2 changed files with 206 additions and 12 deletions
+198 -11
View File
@@ -24,6 +24,9 @@
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#if defined(__linux__)
# include <dirent.h>
#endif
#if !defined(_WIN32)
# include <sys/socket.h>
@@ -38,6 +41,7 @@
# include <sys/types.h>
# include <sys/sysctl.h>
# include <mach/mach_time.h>
# include <mach/mach.h>
#endif
#if defined(_WIN32) && (defined(__i386__) || defined(__x86_64__))
# include <cpuid.h>
@@ -47,7 +51,7 @@
#ifndef FB_API_BASE_URL
# define FB_API_BASE_URL "https://fossbench.net"
#endif
#define FB_VERSION "0.1.5-hotfix3"
#define FB_VERSION "0.1.6"
/* ---------- platform identification (for the banner only) ---------- */
@@ -98,6 +102,7 @@
#if defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <tlhelp32.h>
# include <winhttp.h>
static double now_seconds(void)
{
@@ -259,6 +264,103 @@ static void xfree(void *p)
#endif
}
struct background_metrics {
double average_cpu_percent, peak_cpu_percent;
long available_memory_mb, process_count;
int samples, available;
};
struct cpu_snapshot { uint64_t total, idle; };
static int take_cpu_snapshot(struct cpu_snapshot *s)
{
#if defined(__linux__)
FILE *f = fopen("/proc/stat", "r");
unsigned long long user=0, nice=0, system=0, idle=0, wait=0, irq=0, softirq=0, steal=0;
int n;
if (!f) return 0;
n = fscanf(f, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
&user, &nice, &system, &idle, &wait, &irq, &softirq, &steal);
fclose(f); if (n < 4) return 0;
s->idle = idle + wait;
s->total = user + nice + system + idle + wait + irq + softirq + steal;
return 1;
#elif defined(__APPLE__)
host_cpu_load_info_data_t cpu; mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpu, &count) != KERN_SUCCESS) return 0;
s->idle = cpu.cpu_ticks[CPU_STATE_IDLE];
s->total = cpu.cpu_ticks[CPU_STATE_USER] + cpu.cpu_ticks[CPU_STATE_SYSTEM] + s->idle + cpu.cpu_ticks[CPU_STATE_NICE];
return 1;
#elif defined(_WIN32)
FILETIME idle, kernel, user; ULARGE_INTEGER i, k, u;
if (!GetSystemTimes(&idle, &kernel, &user)) return 0;
i.LowPart=idle.dwLowDateTime; i.HighPart=idle.dwHighDateTime;
k.LowPart=kernel.dwLowDateTime; k.HighPart=kernel.dwHighDateTime;
u.LowPart=user.dwLowDateTime; u.HighPart=user.dwHighDateTime;
s->idle=i.QuadPart; s->total=k.QuadPart+u.QuadPart; return 1;
#else
(void)s; return 0;
#endif
}
static void take_resource_snapshot(long *available_mb, long *processes)
{
*available_mb = -1; *processes = -1;
#if defined(__linux__)
{
FILE *f=fopen("/proc/meminfo","r"); char line[256]; long kb;
if (f) { while (fgets(line,sizeof(line),f)) if (sscanf(line,"MemAvailable: %ld kB",&kb)==1) { *available_mb=kb/1024; break; } fclose(f); }
}
{
DIR *dir=opendir("/proc"); struct dirent *entry; long count=0;
if (dir) { while ((entry=readdir(dir)) != NULL) { const char *p=entry->d_name; if (!*p) continue; while (*p && isdigit((unsigned char)*p)) p++; if (!*p) count++; } closedir(dir); *processes=count; }
}
#elif defined(__APPLE__)
{
vm_statistics_data_t vm; mach_msg_type_number_t count=HOST_VM_INFO_COUNT; vm_size_t page;
if (host_page_size(mach_host_self(),&page)==KERN_SUCCESS && host_statistics(mach_host_self(),HOST_VM_INFO,(host_info_t)&vm,&count)==KERN_SUCCESS)
*available_mb=(long)(((uint64_t)vm.free_count+vm.inactive_count)*page/1024/1024);
}
{
int mib[4]={CTL_KERN,KERN_PROC,KERN_PROC_ALL,0}; size_t bytes=0;
if (sysctl(mib,4,NULL,&bytes,NULL,0)==0) *processes=(long)(bytes/sizeof(struct kinfo_proc));
}
#elif defined(_WIN32)
{
MEMORYSTATUSEX ms; ms.dwLength=sizeof(ms); if (GlobalMemoryStatusEx(&ms)) *available_mb=(long)(ms.ullAvailPhys/1024/1024);
}
{
HANDLE snap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); PROCESSENTRY32 entry; long count=0; entry.dwSize=sizeof(entry);
if (snap!=INVALID_HANDLE_VALUE) { if (Process32First(snap,&entry)) do { count++; } while (Process32Next(snap,&entry)); CloseHandle(snap); *processes=count; }
}
#endif
}
static void sample_background_metrics(struct background_metrics *m, int seconds)
{
struct cpu_snapshot before, after; int i;
memset(m,0,sizeof(*m)); m->available_memory_mb=-1; m->process_count=-1;
printf("\n checking background system activity for %d seconds",seconds); fflush(stdout);
if (!take_cpu_snapshot(&before)) { printf("... unavailable\n"); return; }
for (i=0;i<seconds;i++) {
#if defined(_WIN32)
Sleep(1000);
#else
sleep(1);
#endif
if (take_cpu_snapshot(&after) && after.total>before.total) {
uint64_t total=after.total-before.total, idle=after.idle-before.idle;
double busy=100.0*(double)(total > idle ? total-idle : 0)/(double)total;
m->average_cpu_percent+=busy; if (busy>m->peak_cpu_percent) m->peak_cpu_percent=busy;
m->samples++; before=after;
}
printf("."); fflush(stdout);
}
take_resource_snapshot(&m->available_memory_mb,&m->process_count);
if (m->samples) { m->average_cpu_percent/=m->samples; m->available=1; }
printf(" done\n");
}
/* ---------- workload state ----------
*
* Because every core runs the same kernel simultaneously, each core needs its
@@ -292,6 +394,7 @@ struct system_info {
char model[256];
char operating_system[256];
char compiler[128];
char kernel[128];
long cpu_cores;
long cpu_threads;
long memory_mb;
@@ -350,6 +453,11 @@ static void detect_system_info(struct system_info *info)
#endif
#if defined(__linux__)
{
struct utsname u;
if (uname(&u) == 0)
snprintf(info->kernel, sizeof(info->kernel), "%s %s", u.sysname, u.release);
}
{
static const char *const model_paths[] = {
"/sys/firmware/devicetree/base/compatible",
@@ -432,8 +540,12 @@ static void detect_system_info(struct system_info *info)
if (sysctlbyname("hw.memsize", &mem, &mn, NULL, 0) == 0) info->memory_mb = (long)(mem / 1024 / 1024);
}
{
struct utsname u; if (uname(&u) == 0)
snprintf(info->operating_system, sizeof(info->operating_system), "macOS %s", u.release);
char product[64] = ""; size_t pn = sizeof(product);
struct utsname u;
if (sysctlbyname("kern.osproductversion", product, &pn, NULL, 0) == 0)
snprintf(info->operating_system, sizeof(info->operating_system), "macOS %s", product);
if (uname(&u) == 0)
snprintf(info->kernel, sizeof(info->kernel), "%s %s", u.sysname, u.release);
}
#elif defined(_WIN32)
{
@@ -442,6 +554,21 @@ static void detect_system_info(struct system_info *info)
if (GlobalMemoryStatusEx(&ms))
info->memory_mb = (long)(ms.ullTotalPhys / 1024 / 1024);
}
{
OSVERSIONINFOEXA version;
typedef LONG (WINAPI *rtl_get_version_fn)(OSVERSIONINFOEXA *);
rtl_get_version_fn rtl_get_version = (rtl_get_version_fn)(void *)
GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
memset(&version, 0, sizeof(version)); version.dwOSVersionInfoSize = sizeof(version);
if (rtl_get_version && rtl_get_version(&version) == 0) {
snprintf(info->operating_system, sizeof(info->operating_system),
"Windows %lu.%lu (build %lu)", (unsigned long)version.dwMajorVersion,
(unsigned long)version.dwMinorVersion, (unsigned long)version.dwBuildNumber);
snprintf(info->kernel, sizeof(info->kernel), "NT %lu.%lu build %lu",
(unsigned long)version.dwMajorVersion, (unsigned long)version.dwMinorVersion,
(unsigned long)version.dwBuildNumber);
}
}
#if defined(__i386__) || defined(__x86_64__)
{
/* CPUID leaves 0x80000002-0x80000004 return the 48-byte brand
@@ -990,15 +1117,17 @@ done:
#endif
static int upload_results(const struct system_info *info, double score,
uint64_t duration_ms, const char *token)
double singlecore_score, const struct result *multi,
const struct result *single, uint64_t duration_ms,
const struct background_metrics *background, const char *token)
{
char host[256], port[16], path[512], payload[2048];
char host[256], port[16], path[512], payload[16384];
char auth_header[600];
char cpu[512], model[512], os[512], compiler[256];
char cpu[512], model[512], os[512], compiler[256], kernel[256];
const char *base = FB_API_BASE_URL, *p, *slash, *colon;
int use_tls, status = 0, payload_len;
#if !defined(_WIN32)
char request[4096], response[512];
char request[20000], response[512];
struct addrinfo hints, *addresses = NULL, *a;
SSL_CTX *tls_ctx = NULL;
SSL *tls = NULL;
@@ -1034,15 +1163,53 @@ static int upload_results(const struct system_info *info, double score,
json_escape(info->model, model, sizeof(model));
json_escape(info->operating_system, os, sizeof(os));
json_escape(info->compiler, compiler, sizeof(compiler));
json_escape(info->kernel, kernel, sizeof(kernel));
/* "fossmark_version" is the API's field name, fixed by the server
* contract; it does not track this client's own product name. */
payload_len = snprintf(payload, sizeof(payload),
"{\"cpu\":\"%s\",\"model\":\"%s\",\"cpu_cores\":%ld,\"cpu_threads\":%ld,"
"\"memory_mb\":%ld,\"operating_system\":\"%s\",\"compiler\":\"%s\","
"\"fossmark_version\":\"%s\",\"score\":%.2f,\"duration_ms\":%llu}",
"\"fossmark_version\":\"%s\",\"score\":%.17g,\"duration_ms\":%llu,"
"\"score_details\":{\"scoring_method\":\"weighted_geometric_mean\","
"\"target_score\":%.17g,\"multicore_score\":%.17g,\"singlecore_score\":%.17g,"
"\"minimum_test_seconds\":%.17g,\"repeats\":%d,"
"\"system_environment\":{\"kernel\":\"%s\",\"sample_seconds\":%d,"
"\"background_cpu_average_percent\":%.17g,\"background_cpu_peak_percent\":%.17g,"
"\"available_memory_mb\":%ld,\"process_count\":%ld},\"tests\":[",
cpu, model, info->cpu_cores, info->cpu_threads, info->memory_mb, os, compiler,
FB_VERSION, score, (unsigned long long)duration_ms);
FB_VERSION, score, (unsigned long long)duration_ms, FB_TARGET_SCORE, score,
singlecore_score, MIN_SECONDS, REPEATS, kernel, background->samples,
background->average_cpu_percent, background->peak_cpu_percent,
background->available_memory_mb, background->process_count);
if (payload_len < 0 || (size_t)payload_len >= sizeof(payload)) return 0;
{
size_t used = (size_t)payload_len;
size_t i;
for (i = 0; i < NTESTS; i++) {
int n = snprintf(payload + used, sizeof(payload) - used,
"%s{\"name\":\"%s\",\"detail\":\"%s\",\"unit\":\"%s\","
"\"start_iterations\":%llu,\"work_per_iteration\":%.17g,"
"\"reference_rate\":%.17g,\"weight\":%.17g,"
"\"multicore\":{\"display_metric\":%.17g,\"rate\":%.17g,\"score\":%.17g,"
"\"seconds\":%.17g,\"iterations\":%llu,\"threads\":%d,\"checksum\":\"%llu\"},"
"\"singlecore\":{\"display_metric\":%.17g,\"rate\":%.17g,\"score\":%.17g,"
"\"seconds\":%.17g,\"iterations\":%llu,\"threads\":%d,\"checksum\":\"%llu\"}}",
i ? "," : "", tests[i].name, tests[i].detail, tests[i].unit,
(unsigned long long)tests[i].start_n, tests[i].work_per_n,
tests[i].ref_rate, tests[i].weight,
display_metric(&tests[i], &multi[i]), multi[i].rate, multi[i].score,
multi[i].seconds, (unsigned long long)multi[i].iters, multi[i].threads,
(unsigned long long)multi[i].checksum,
display_metric(&tests[i], &single[i]), single[i].rate, single[i].score,
single[i].seconds, (unsigned long long)single[i].iters, single[i].threads,
(unsigned long long)single[i].checksum);
if (n < 0 || (size_t)n >= sizeof(payload) - used) return 0;
used += (size_t)n;
}
if (used + 3 >= sizeof(payload)) return 0;
memcpy(payload + used, "]}}", 4);
payload_len = (int)(used + 3);
}
auth_header[0] = '\0';
if (token && token[0]) {
@@ -1149,6 +1316,7 @@ static void print_header(const struct system_info *info)
printf(" cores: %ld physical / %ld threads\n", info->cpu_cores, info->cpu_threads);
printf(" memory: %ld MB\n", info->memory_mb);
printf(" OS: %s (%s)\n", info->operating_system, FB_ARCH);
printf(" kernel: %s\n", info->kernel[0] ? info->kernel : "unknown");
printf(" compiler: %s\n", info->compiler);
printf("\n");
printf(" %-24s %12s %-11s %8s %9s\n",
@@ -1161,12 +1329,14 @@ int main(int argc, char **argv)
{
struct result multi[NTESTS], single[NTESTS];
struct system_info system_info;
struct background_metrics background;
double multi_log_sum = 0.0, single_log_sum = 0.0;
double weight_sum = 0.0;
double benchmark_started, multicore_score, singlecore_score;
uint64_t duration_ms;
int verbose = 0;
int upload_mode = 0; /* 0 = ask, 1 = force upload, 2 = force no upload */
int system_check = 1;
size_t i;
for (i = 1; i < (size_t)argc; i++) {
@@ -1185,9 +1355,11 @@ int main(int argc, char **argv)
return 1;
}
upload_mode = 2;
} else if (strcmp(argv[i], "--no-system-check") == 0) {
system_check = 0;
} else if (strcmp(argv[i], "-h") == 0 ||
strcmp(argv[i], "--help") == 0) {
printf("usage: %s [-v|--verbose] [--upload|--noupload]\n", argv[0]);
printf("usage: %s [-v|--verbose] [--upload|--noupload] [--no-system-check]\n", argv[0]);
return 0;
} else {
fprintf(stderr, "fossbench: unknown option '%s'\n",
@@ -1207,6 +1379,20 @@ int main(int argc, char **argv)
g_ncores = n > 0 ? n : 1;
}
detect_system_info(&system_info);
memset(&background, 0, sizeof(background));
background.available_memory_mb = background.process_count = -1;
if (system_check) {
sample_background_metrics(&background, 10);
if (background.available) {
printf(" background CPU: %.1f%% average / %.1f%% peak\n",
background.average_cpu_percent, background.peak_cpu_percent);
if (background.available_memory_mb >= 0)
printf(" available memory: %ld MB of %ld MB\n", background.available_memory_mb, system_info.memory_mb);
if (background.process_count >= 0) printf(" processes: %ld\n", background.process_count);
if (background.average_cpu_percent >= 10.0)
printf(" warning: background CPU activity may reduce benchmark scores\n");
}
}
benchmark_started = now_seconds();
printf("\n preparing workloads...");
@@ -1299,7 +1485,8 @@ int main(int argc, char **argv)
}
if (do_upload)
upload_results(&system_info, multicore_score, duration_ms, token);
upload_results(&system_info, multicore_score, singlecore_score,
multi, single, duration_ms, &background, token);
}
return 0;
}