mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Add machineinfo_struct to query and report system and CPU information in speed test
This commit is contained in:
parent
ef5c5471dc
commit
3288c8cc8c
4 changed files with 772 additions and 124 deletions
|
|
@ -462,5 +462,40 @@ void app_default_conf(char* dst, size_t dst_size, const char* argv0);
|
|||
*/
|
||||
void app_signal_handler(int signum);
|
||||
|
||||
/****************************************************************************/
|
||||
/* machine */
|
||||
|
||||
#define MACHINEINFO_MAX 256
|
||||
|
||||
/**
|
||||
* Host machine and CPU information.
|
||||
*/
|
||||
struct machineinfo_struct {
|
||||
char cpu_brand[MACHINEINFO_MAX]; /**< CPU model brand name. */
|
||||
char cpu_vendor[MACHINEINFO_MAX]; /**< CPU vendor string (e.g. GenuineIntel, AuthenticAMD). */
|
||||
char cpu_arch[MACHINEINFO_MAX]; /**< CPU architecture string. */
|
||||
char cpu_clock[MACHINEINFO_MAX]; /**< CPU clock frequency description. */
|
||||
unsigned cpu_family; /**< CPU family id. */
|
||||
unsigned cpu_model; /**< CPU model id. */
|
||||
unsigned cpu_stepping; /**< CPU stepping id. */
|
||||
unsigned cpu_cores_physical; /**< Number of physical CPU cores (0 if unavailable). */
|
||||
unsigned cpu_cores_logical; /**< Number of logical CPU threads (0 if unavailable). */
|
||||
uint64_t cache_l1_data; /**< L1 data cache in bytes (0 if unavailable). */
|
||||
uint64_t cache_l1_instruction; /**< L1 instruction cache in bytes (0 if unavailable). */
|
||||
uint64_t cache_l2; /**< L2 cache in bytes (0 if unavailable). */
|
||||
uint64_t cache_l3; /**< L3 cache in bytes (0 if unavailable). */
|
||||
uint64_t memory_total_bytes; /**< Total system RAM in bytes (0 if unavailable). */
|
||||
uint32_t memory_page_size; /**< Page size in bytes (0 if unavailable). */
|
||||
char os_name[MACHINEINFO_MAX]; /**< Operating system name and kernel version. */
|
||||
char os_distribution[MACHINEINFO_MAX]; /**< Operating system distribution name. */
|
||||
char system_model[MACHINEINFO_MAX]; /**< Motherboard or system model name. */
|
||||
char hypervisor[MACHINEINFO_MAX]; /**< Hypervisor name (empty if bare metal). */
|
||||
};
|
||||
|
||||
/**
|
||||
* Query host machine information.
|
||||
*/
|
||||
void machineinfo(struct machineinfo_struct* info);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#ifdef __MINGW32__ /* Only for MingW */
|
||||
|
||||
#include "support.h"
|
||||
#include "raid/cpu.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* global */
|
||||
|
|
@ -1931,5 +1932,163 @@ void app_default_conf(char* conf, size_t conf_size, const char* argv0)
|
|||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* machine */
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
static void format_freq(char* buf, size_t size, unsigned mhz)
|
||||
{
|
||||
if (mhz >= 1000)
|
||||
snprintf(buf, size, "%.2f GHz", mhz / 1000.0);
|
||||
else
|
||||
snprintf(buf, size, "%u MHz", mhz);
|
||||
}
|
||||
#endif
|
||||
|
||||
void machineinfo(struct machineinfo_struct* info)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
#if defined(__x86_64__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "x86_64");
|
||||
#elif defined(__i386__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "x86_32");
|
||||
#elif defined(__aarch64__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "aarch64");
|
||||
#elif defined(__arm__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "arm");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
raid_cpu_info(info->cpu_vendor, &info->cpu_family, &info->cpu_model);
|
||||
|
||||
uint32_t reg[4];
|
||||
raid_cpuid(1, 0, reg);
|
||||
info->cpu_stepping = reg[0] & 0xF;
|
||||
|
||||
/* brand string from extended CPUID leaves 0x80000002..0x80000004 */
|
||||
raid_cpuid(0x80000000, 0, reg);
|
||||
if (reg[0] >= 0x80000004) {
|
||||
raid_cpuid(0x80000002, 0, reg);
|
||||
memcpy(info->cpu_brand + 0, reg, sizeof(reg));
|
||||
raid_cpuid(0x80000003, 0, reg);
|
||||
memcpy(info->cpu_brand + 16, reg, sizeof(reg));
|
||||
raid_cpuid(0x80000004, 0, reg);
|
||||
memcpy(info->cpu_brand + 32, reg, sizeof(reg));
|
||||
info->cpu_brand[48] = 0;
|
||||
strtrim(info->cpu_brand);
|
||||
}
|
||||
|
||||
/* base and maximum frequency from CPUID leaf 0x16 */
|
||||
raid_cpuid(0, 0, reg);
|
||||
if (reg[0] >= 0x16) {
|
||||
raid_cpuid(0x16, 0, reg);
|
||||
unsigned base_mhz = reg[0] & 0xFFFF;
|
||||
unsigned max_mhz = reg[1] & 0xFFFF;
|
||||
if (base_mhz > 0 && max_mhz > 0 && base_mhz != max_mhz) {
|
||||
char base_str[32], max_str[32];
|
||||
format_freq(base_str, sizeof(base_str), base_mhz);
|
||||
format_freq(max_str, sizeof(max_str), max_mhz);
|
||||
snprintf(info->cpu_clock, sizeof(info->cpu_clock), "%s (max %s)", base_str, max_str);
|
||||
} else if (base_mhz > 0) {
|
||||
format_freq(info->cpu_clock, sizeof(info->cpu_clock), base_mhz);
|
||||
} else if (max_mhz > 0) {
|
||||
format_freq(info->cpu_clock, sizeof(info->cpu_clock), max_mhz);
|
||||
}
|
||||
}
|
||||
|
||||
/* hypervisor detection */
|
||||
raid_cpuid(1, 0, reg);
|
||||
if (reg[2] & (1U << 31)) {
|
||||
raid_cpuid(0x40000000, 0, reg);
|
||||
memcpy(info->hypervisor + 0, ®[1], 4);
|
||||
memcpy(info->hypervisor + 4, ®[2], 4);
|
||||
memcpy(info->hypervisor + 8, ®[3], 4);
|
||||
info->hypervisor[12] = 0;
|
||||
strtrim(info->hypervisor);
|
||||
}
|
||||
#endif
|
||||
|
||||
MEMORYSTATUSEX ms;
|
||||
memset(&ms, 0, sizeof(ms));
|
||||
ms.dwLength = sizeof(ms);
|
||||
if (GlobalMemoryStatusEx(&ms)) {
|
||||
info->memory_total_bytes = ms.ullTotalPhys;
|
||||
}
|
||||
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
info->memory_page_size = si.dwPageSize;
|
||||
info->cpu_cores_logical = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
|
||||
|
||||
/* Query processor topology and cache hierarchy via GetLogicalProcessorInformationEx */
|
||||
DWORD len = 0;
|
||||
GetLogicalProcessorInformationEx(RelationAll, 0, &len);
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && len > 0) {
|
||||
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* buf = malloc_nofail(len);
|
||||
if (GetLogicalProcessorInformationEx(RelationAll, buf, &len)) {
|
||||
DWORD offset = 0;
|
||||
while (offset < len) {
|
||||
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* info_ex = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)((char*)buf + offset);
|
||||
if (info_ex->Relationship == RelationProcessorCore) {
|
||||
++info->cpu_cores_physical;
|
||||
} else if (info_ex->Relationship == RelationCache) {
|
||||
CACHE_RELATIONSHIP* cache = &info_ex->Cache;
|
||||
/*
|
||||
* Select the coherent cache hierarchy associated with the primary
|
||||
* core (Core 0). This prevents mixing cache levels from different
|
||||
* core types on hybrid architectures (e.g. L1 from P-core, L2 from E-cluster).
|
||||
*/
|
||||
int matches_core0 = 0;
|
||||
if (cache->GroupCount == 0) {
|
||||
if (cache->GroupMask.Group == 0 && (cache->GroupMask.Mask & 1) != 0)
|
||||
matches_core0 = 1;
|
||||
} else {
|
||||
for (WORD g = 0; g < cache->GroupCount; ++g) {
|
||||
if (cache->GroupMasks[g].Group == 0 && (cache->GroupMasks[g].Mask & 1) != 0) {
|
||||
matches_core0 = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches_core0) {
|
||||
if (cache->Level == 1) {
|
||||
if (cache->Type == CacheData || cache->Type == CacheUnified)
|
||||
info->cache_l1_data = cache->CacheSize;
|
||||
else if (cache->Type == CacheInstruction)
|
||||
info->cache_l1_instruction = cache->CacheSize;
|
||||
} else if (cache->Level == 2) {
|
||||
info->cache_l2 = cache->CacheSize;
|
||||
} else if (cache->Level == 3) {
|
||||
info->cache_l3 = cache->CacheSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info_ex->Size == 0)
|
||||
break;
|
||||
offset += info_ex->Size;
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* OS version via RtlGetVersion */
|
||||
typedef LONG (WINAPI* RtlGetVersionPtr)(LPOSVERSIONINFOW);
|
||||
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
|
||||
if (ntdll) {
|
||||
RtlGetVersionPtr pRtlGetVersion = (RtlGetVersionPtr)(void*)GetProcAddress(ntdll, "RtlGetVersion");
|
||||
if (pRtlGetVersion) {
|
||||
OSVERSIONINFOW vi;
|
||||
memset(&vi, 0, sizeof(vi));
|
||||
vi.dwOSVersionInfoSize = sizeof(vi);
|
||||
if (pRtlGetVersion(&vi) == 0) {
|
||||
snprintf(info->os_name, sizeof(info->os_name), "Windows %lu.%lu (Build %lu)",
|
||||
(unsigned long)vi.dwMajorVersion, (unsigned long)vi.dwMinorVersion, (unsigned long)vi.dwBuildNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
162
cmdline/speed.c
162
cmdline/speed.c
|
|
@ -2278,14 +2278,16 @@ void speed_rec(int nd, void** v, int size, int delta, int period)
|
|||
void speed_affinity(void)
|
||||
{
|
||||
#if HAVE_LINUX_DEVICE
|
||||
int num_cpus = sysconf(_SC_NPROCESSORS_CONF);
|
||||
int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (num_cpus <= 0)
|
||||
num_cpus = sysconf(_SC_NPROCESSORS_CONF);
|
||||
if (num_cpus <= 0)
|
||||
return;
|
||||
|
||||
printf("Machine has %d cores\n", num_cpus);
|
||||
|
||||
int cpu = os_get_optimal_cpu();
|
||||
if (cpu < 0)
|
||||
if (cpu < 0 || cpu >= CPU_SETSIZE)
|
||||
return;
|
||||
|
||||
cpu_set_t mask;
|
||||
|
|
@ -2293,7 +2295,7 @@ void speed_affinity(void)
|
|||
CPU_SET(cpu, &mask);
|
||||
|
||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == 0) {
|
||||
printf("Running on core %d\n", cpu);
|
||||
printf("Running on cpu %d\n", cpu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -2304,7 +2306,7 @@ static int check_cpu_feature(const char* name)
|
|||
int value = 0;
|
||||
size_t len = sizeof(value);
|
||||
|
||||
if (sysctlbyname(name, &value, &len, NULL, 0) == 0) {
|
||||
if (sysctlbyname(name, &value, &len, 0, 0) == 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
@ -2319,24 +2321,26 @@ static const char* decode_cpufamily(uint32_t family)
|
|||
case 0x462504d2 : return "Lightning/Thunder (A13)";
|
||||
case 0x1b588bb3 : return "Firestorm/Icestorm (M1/A14)";
|
||||
case 0xda33d83d : return "Blizzard/Avalanche (M2/A15)";
|
||||
case 0x8765edea : return "Everest/Sawtooth (M3/A16)";
|
||||
case 0xfa33415e : return "Ibiza (M4/A17)";
|
||||
case 0x8765edea : return "Everest/Sawtooth (A16)";
|
||||
case 0xfa33415e : return "Ibiza (M3)";
|
||||
case 0x72015832 : return "Palma (M3 Pro)";
|
||||
case 0x17d5e360 : return "Lobos (M3 Max)";
|
||||
case 0x2876f5b5 : return "Coll (A17 Pro)";
|
||||
case 0x6f5129ac : return "Donan (M4)";
|
||||
case 0x1774f74e : return "Brava (M4 Pro)";
|
||||
case 0x524c529b : return "Hidra (M4 Max)";
|
||||
case 0x7843b821 : return "Tahiti (A18)";
|
||||
case 0x6f64249a : return "Tupai (A18 Pro)";
|
||||
case 0 : return "Unavailable";
|
||||
default : return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void print_apple(void)
|
||||
void print_apple(const struct machineinfo_struct* info)
|
||||
{
|
||||
char brand[128] = "Unknown Apple Silicon";
|
||||
size_t len = sizeof(brand);
|
||||
if (sysctlbyname("machdep.cpu.brand_string", &brand, &len, NULL, 0) != 0) {
|
||||
sysctlbyname("hw.model", &brand, &len, NULL, 0);
|
||||
}
|
||||
|
||||
uint32_t family = 0;
|
||||
len = sizeof(uint32_t);
|
||||
sysctlbyname("hw.cpufamily", &family, &len, NULL, 0);
|
||||
size_t len = sizeof(uint32_t);
|
||||
sysctlbyname("hw.cpufamily", &family, &len, 0, 0);
|
||||
|
||||
int has_aes = check_cpu_feature("hw.optional.arm.FEAT_AES");
|
||||
int has_dotprod = check_cpu_feature("hw.optional.arm.FEAT_DotProd");
|
||||
|
|
@ -2344,44 +2348,27 @@ void print_apple(void)
|
|||
int has_sve = check_cpu_feature("hw.optional.arm.FEAT_SVE");
|
||||
int has_sme = check_cpu_feature("hw.optional.arm.FEAT_SME");
|
||||
|
||||
const char* name = info->cpu_brand[0] ? info->cpu_brand : (info->system_model[0] ? info->system_model : "Apple Silicon");
|
||||
|
||||
printf("CPU %s, family %s (0x%08x), flags%s%s%s%s%s\n",
|
||||
brand, decode_cpufamily(family), family,
|
||||
name, decode_cpufamily(family), family,
|
||||
has_aes ? " aes" : "",
|
||||
has_dotprod ? " dotprod" : "",
|
||||
has_sha3 ? " sha3" : "",
|
||||
has_sve ? " sve" : "",
|
||||
has_sme ? " sme" : ""
|
||||
);
|
||||
|
||||
unsigned long l1_d_bytes = 0;
|
||||
unsigned long l2_bytes = 0;
|
||||
unsigned long l3_bytes = 0;
|
||||
|
||||
len = sizeof(unsigned long);
|
||||
sysctlbyname("hw.l1dcachesize", &l1_d_bytes, &len, 0, 0);
|
||||
len = sizeof(unsigned long);
|
||||
sysctlbyname("hw.l2cachesize", &l2_bytes, &len, 0, 0);
|
||||
len = sizeof(unsigned long);
|
||||
sysctlbyname("hw.l3cachesize", &l3_bytes, &len, 0, 0);
|
||||
|
||||
printf("Cache L1 Data %lu KB, L2 %lu KB", l1_d_bytes / 1024, l2_bytes / 1024);
|
||||
if (l3_bytes > 0) {
|
||||
printf(", L3 %lu KB", l3_bytes / 1024);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
void print_intel(void)
|
||||
void print_intel(const struct machineinfo_struct* info)
|
||||
{
|
||||
char vendor[CPU_VENDOR_MAX];
|
||||
unsigned family;
|
||||
unsigned model;
|
||||
|
||||
raid_cpu_info(vendor, &family, &model);
|
||||
|
||||
printf("CPU %s, family %u, model %u (0x%x), flags%s%s%s%s%s%s%s%s%s%s\n", vendor, family, model, model,
|
||||
printf("CPU %s, family %u (0x%x), model %u (0x%x), stepping %u, flags%s%s%s%s%s%s%s%s%s%s\n",
|
||||
info->cpu_vendor[0] ? info->cpu_vendor : "x86",
|
||||
info->cpu_family, info->cpu_family,
|
||||
info->cpu_model, info->cpu_model,
|
||||
info->cpu_stepping,
|
||||
raid_cpu_has_sse2() ? " sse2" : "",
|
||||
raid_cpu_has_ssse3() ? " ssse3" : "",
|
||||
raid_cpu_has_crc32() ? " crc32" : "",
|
||||
|
|
@ -2393,18 +2380,6 @@ void print_intel(void)
|
|||
raid_cpu_has_slow_extendedreg() ? " slowext" : "",
|
||||
raid_cpu_has_avx512bw() && raid_cpu_has_slow_avx512() ? " slowavx512" : ""
|
||||
);
|
||||
|
||||
#ifdef __linux__
|
||||
long l1_d_bytes = sysconf(_SC_LEVEL1_DCACHE_SIZE);
|
||||
long l2_bytes = sysconf(_SC_LEVEL2_CACHE_SIZE);
|
||||
long l3_bytes = sysconf(_SC_LEVEL3_CACHE_SIZE);
|
||||
|
||||
printf("Cache L1 Data %lu KB, L2 %lu KB", l1_d_bytes / 1024, l2_bytes / 1024);
|
||||
if (l3_bytes > 0) {
|
||||
printf(", L3 %lu KB", l3_bytes / 1024);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -2415,6 +2390,9 @@ void speed(int period, int nd, int size)
|
|||
int nv;
|
||||
void* v_alloc;
|
||||
void** v;
|
||||
struct machineinfo_struct minfo;
|
||||
|
||||
machineinfo(&minfo);
|
||||
|
||||
if (nd < 0)
|
||||
nd = 8; /* default */
|
||||
|
|
@ -2448,20 +2426,20 @@ void speed(int period, int nd, int size)
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
print_intel();
|
||||
print_intel(&minfo);
|
||||
#elif defined(__aarch64__) && defined(__APPLE__)
|
||||
print_apple();
|
||||
print_apple(&minfo);
|
||||
#elif defined(__aarch64__)
|
||||
#if defined(CONFIG_NEON)
|
||||
printf("CPU 64-bit ARM (AArch64), flags neon\n");
|
||||
printf("CPU %s, flags neon\n", minfo.cpu_brand[0] ? minfo.cpu_brand : "64-bit ARM (AArch64)");
|
||||
#else
|
||||
printf("CPU 64-bit ARM (AArch64)\n");
|
||||
printf("CPU %s\n", minfo.cpu_brand[0] ? minfo.cpu_brand : "64-bit ARM (AArch64)");
|
||||
#endif
|
||||
#elif defined(__arm__)
|
||||
#if defined(CONFIG_NEON32)
|
||||
printf("CPU 32-bit ARM, flags neon\n");
|
||||
printf("CPU %s, flags neon\n", minfo.cpu_brand[0] ? minfo.cpu_brand : "32-bit ARM");
|
||||
#else
|
||||
printf("CPU 32-bit ARM\n");
|
||||
printf("CPU %s\n", minfo.cpu_brand[0] ? minfo.cpu_brand : "32-bit ARM");
|
||||
#endif
|
||||
#elif defined(__powerpc64__)
|
||||
printf("CPU 64-bit PowerPC\n");
|
||||
|
|
@ -2474,12 +2452,60 @@ void speed(int period, int nd, int size)
|
|||
#else
|
||||
printf("CPU of unknown architecture\n");
|
||||
#endif
|
||||
|
||||
if (minfo.cpu_brand[0] != 0) {
|
||||
const char* p = minfo.cpu_brand;
|
||||
int in_space = 0;
|
||||
|
||||
printf("Model ");
|
||||
while (*p != 0) {
|
||||
if (*p == ' ') {
|
||||
if (!in_space) {
|
||||
putchar(' ');
|
||||
in_space = 1;
|
||||
}
|
||||
} else {
|
||||
putchar(*p);
|
||||
in_space = 0;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (minfo.cpu_clock[0] != 0) {
|
||||
printf("Clock %s\n", minfo.cpu_clock);
|
||||
}
|
||||
|
||||
if (minfo.cache_l1_data > 0 || minfo.cache_l2 > 0) {
|
||||
printf("Cache L1 Data %" PRIu64 " KB, L2 %" PRIu64 " KB",
|
||||
minfo.cache_l1_data / 1024, minfo.cache_l2 / 1024);
|
||||
if (minfo.cache_l3 > 0) {
|
||||
printf(", L3 %" PRIu64 " KB", minfo.cache_l3 / 1024);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
speed_affinity();
|
||||
|
||||
if (minfo.memory_total_bytes > 0) {
|
||||
printf("Memory %" PRIu64 " MB", minfo.memory_total_bytes / (1024 * 1024));
|
||||
#if WORDS_BIGENDIAN
|
||||
printf("Memory is big-endian %d-bit\n", (int)sizeof(void*) * 8);
|
||||
printf(", big-endian %d-bit", (int)sizeof(void*) * 8);
|
||||
#else
|
||||
printf("Memory is little-endian %d-bit\n", (int)sizeof(void*) * 8);
|
||||
printf(", little-endian %d-bit", (int)sizeof(void*) * 8);
|
||||
#endif
|
||||
if (minfo.memory_page_size > 0)
|
||||
printf(", page %u KB", minfo.memory_page_size / 1024);
|
||||
printf("\n");
|
||||
} else {
|
||||
#if WORDS_BIGENDIAN
|
||||
printf("Memory is big-endian %d-bit\n", (int)sizeof(void*) * 8);
|
||||
#else
|
||||
printf("Memory is little-endian %d-bit\n", (int)sizeof(void*) * 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
printf("128-bit integers are supported\n");
|
||||
#else
|
||||
|
|
@ -2496,6 +2522,20 @@ void speed(int period, int nd, int size)
|
|||
printf("Does not support nanosecond timestamps\n");
|
||||
#endif
|
||||
|
||||
if (minfo.os_distribution[0] != 0 && minfo.os_name[0] != 0) {
|
||||
printf("Host %s (%s)\n", minfo.os_distribution, minfo.os_name);
|
||||
} else if (minfo.os_name[0] != 0) {
|
||||
printf("Host %s\n", minfo.os_name);
|
||||
}
|
||||
|
||||
if (minfo.system_model[0] != 0 || minfo.hypervisor[0] != 0) {
|
||||
printf("System %s%s%s%s\n",
|
||||
minfo.system_model[0] ? minfo.system_model : "Generic",
|
||||
minfo.hypervisor[0] ? " [" : "",
|
||||
minfo.hypervisor[0] ? minfo.hypervisor : "",
|
||||
minfo.hypervisor[0] ? "]" : "");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
printf("Speed test using %u data buffers of %u bytes, for a total of %u KiB.\n", nd, size, nd * size / KIBI);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "app.h"
|
||||
#include "support.h"
|
||||
#include "raid/cpu.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* global */
|
||||
|
|
@ -502,70 +503,106 @@ static long long syslong(const char* path)
|
|||
}
|
||||
|
||||
/*
|
||||
* Parse a sysfs hexadecimal bitmask string (e.g. thread_siblings).
|
||||
* Parse Linux cpulist format (e.g. "0-3,5,8-11") into an array of CPU IDs.
|
||||
*
|
||||
* Linux sysfs outputs bitmasks in big-endian 32-bit hex word groups
|
||||
* (highest CPUs on the left, lowest CPUs on the right).
|
||||
*
|
||||
* Left-shifting digits into a 64-bit unsigned long long operates modulo 2^64,
|
||||
* discarding any high-order hex words (CPUs >= 64) and leaving bits 0..63
|
||||
* containing the exact mask for CPUs 0..63. Since CPUS_MAX is 64, this mask
|
||||
* is always accurate for all evaluated CPUs even on systems with > 64 CPUs.
|
||||
* Returns the total number of CPUs in the list. Stores up to max_cpus entries.
|
||||
*/
|
||||
static unsigned long long syshex(const char* path)
|
||||
static unsigned parse_cpulist(const char* str, unsigned* cpus, unsigned max_cpus)
|
||||
{
|
||||
char buf[1152]; /* enough for 1024 CPUs */
|
||||
if (sysattr(path, buf, sizeof(buf)) != 0)
|
||||
return 0;
|
||||
const char* p = str;
|
||||
unsigned count = 0;
|
||||
|
||||
while (*p != 0) {
|
||||
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
|
||||
++p;
|
||||
if (*p == 0)
|
||||
break;
|
||||
|
||||
char* end;
|
||||
unsigned long start = strtoul(p, &end, 10);
|
||||
if (end == p)
|
||||
break;
|
||||
|
||||
unsigned long stop = start;
|
||||
p = end;
|
||||
if (*p == '-') {
|
||||
++p;
|
||||
stop = strtoul(p, &end, 10);
|
||||
if (end == p)
|
||||
break;
|
||||
p = end;
|
||||
}
|
||||
|
||||
if (stop < start || stop >= CPU_SETSIZE)
|
||||
break;
|
||||
|
||||
for (unsigned long i = start;; ++i) {
|
||||
if (cpus != 0 && count < max_cpus)
|
||||
cpus[count] = (unsigned)i;
|
||||
++count;
|
||||
|
||||
if (i == stop)
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned long long v = 0;
|
||||
for (char* p = buf; *p != 0 && *p != '\n'; ++p) {
|
||||
if (*p == ',')
|
||||
continue;
|
||||
|
||||
v <<= 4;
|
||||
if (*p >= '0' && *p <= '9')
|
||||
v |= *p - '0';
|
||||
else if (*p >= 'a' && *p <= 'f')
|
||||
v |= *p - 'a' + 10;
|
||||
else if (*p >= 'A' && *p <= 'F')
|
||||
v |= *p - 'A' + 10;
|
||||
else
|
||||
break; /* hit unexpected character */
|
||||
++p;
|
||||
}
|
||||
|
||||
return v;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int cpu_is_online(int i)
|
||||
/*
|
||||
* Read online CPU IDs from /sys/devices/system/cpu/online.
|
||||
*
|
||||
* Falls back to consecutive 0..N-1 IDs if sysfs is unavailable.
|
||||
* Returns the total number of online CPUs. Stores up to max_cpus entries in cpus.
|
||||
*/
|
||||
static unsigned read_online_cpus(unsigned* cpus, unsigned max_cpus)
|
||||
{
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", i);
|
||||
long long v = syslong(path);
|
||||
if (v < 0)
|
||||
return 1; /* cpu0 has no "online" knob — always on */
|
||||
return v;
|
||||
char buf[1024];
|
||||
if (sysattr("/sys/devices/system/cpu/online", buf, sizeof(buf)) == 0) {
|
||||
unsigned count = parse_cpulist(buf, cpus, max_cpus);
|
||||
if (count > 0)
|
||||
return count;
|
||||
}
|
||||
|
||||
/* fallback: consecutive IDs from 0 */
|
||||
long n = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (n <= 0)
|
||||
n = sysconf(_SC_NPROCESSORS_CONF);
|
||||
if (n <= 0)
|
||||
n = 1;
|
||||
|
||||
unsigned count = (unsigned)n;
|
||||
unsigned store = count < max_cpus ? count : max_cpus;
|
||||
for (unsigned i = 0; i < store; ++i) {
|
||||
if (cpus != 0)
|
||||
cpus[i] = i;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if the specified cpu is the lowest-numbered logical CPU in its
|
||||
* physical core's SMT sibling set.
|
||||
*
|
||||
* thread_siblings is a hex bitmask where bit N = logical cpu N.
|
||||
* The lowest set bit identifies the primary sibling.
|
||||
* Safe for up to 64 logical CPUs (fits in unsigned long).
|
||||
*/
|
||||
static int cpu_is_primary_sibling(int i)
|
||||
static int cpu_is_primary_sibling(unsigned i)
|
||||
{
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i);
|
||||
unsigned long long mask = syshex(path);
|
||||
if (mask == 0) /* assume primary */
|
||||
return 1;
|
||||
return __builtin_ctzll(mask) == i;
|
||||
}
|
||||
char buf[256];
|
||||
|
||||
#define CPUS_MAX 64
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/topology/thread_siblings_list", i);
|
||||
if (sysattr(path, buf, sizeof(buf)) != 0)
|
||||
return 1; /* assume primary */
|
||||
|
||||
unsigned siblings[16];
|
||||
unsigned count = parse_cpulist(buf, siblings, sizeof(siblings) / sizeof(siblings[0]));
|
||||
if (count == 0)
|
||||
return 1;
|
||||
|
||||
return i == siblings[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* os_get_optimal_cpu() – returns the logical CPU index of the "fastest"
|
||||
|
|
@ -578,27 +615,25 @@ static int cpu_is_primary_sibling(int i)
|
|||
int os_get_optimal_cpu(void)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
int num_cpus = sysconf(_SC_NPROCESSORS_CONF);
|
||||
if (num_cpus <= 0)
|
||||
unsigned cpus[CPU_SETSIZE];
|
||||
unsigned total_cpus = read_online_cpus(cpus, CPU_SETSIZE);
|
||||
unsigned num_cpus = total_cpus < CPU_SETSIZE ? total_cpus : CPU_SETSIZE;
|
||||
if (num_cpus == 0)
|
||||
return -1;
|
||||
if (num_cpus > CPUS_MAX)
|
||||
num_cpus = CPUS_MAX;
|
||||
|
||||
/* ACPI CPPC: highest_perf with nominal_perf tiebreaker */
|
||||
long long max_highest = -1;
|
||||
long long max_nominal = -1;
|
||||
int best_cppc = -1;
|
||||
for (int i = 0; i < num_cpus; ++i) {
|
||||
if (!cpu_is_online(i))
|
||||
continue;
|
||||
for (unsigned k = 0; k < num_cpus; ++k) {
|
||||
unsigned i = cpus[k];
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/acpi_cppc/highest_perf", i);
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/acpi_cppc/highest_perf", i);
|
||||
long long hp = syslong(path);
|
||||
if (hp < 0)
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/acpi_cppc/nominal_perf", i);
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/acpi_cppc/nominal_perf", i);
|
||||
long long np = syslong(path);
|
||||
/* np may be missing */
|
||||
|
||||
|
|
@ -620,11 +655,10 @@ int os_get_optimal_cpu(void)
|
|||
|
||||
/* find the global maximum frequency among online CPUs */
|
||||
long long global_max_freq = -1;
|
||||
for (int i = 0; i < num_cpus; ++i) {
|
||||
if (!cpu_is_online(i))
|
||||
continue;
|
||||
for (unsigned k = 0; k < num_cpus; ++k) {
|
||||
unsigned i = cpus[k];
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i);
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
|
||||
long long freq = syslong(path);
|
||||
if (freq < 0)
|
||||
continue;
|
||||
|
|
@ -638,12 +672,11 @@ int os_get_optimal_cpu(void)
|
|||
int best_any = -1; /* any matching CPU, not CPU 0 */
|
||||
int best_c0 = -1; /* CPU 0 fallback (always a primary sibling) */
|
||||
|
||||
for (int i = 0; i < num_cpus; ++i) {
|
||||
if (!cpu_is_online(i))
|
||||
continue;
|
||||
for (unsigned k = 0; k < num_cpus; ++k) {
|
||||
unsigned i = cpus[k];
|
||||
|
||||
if (global_max_freq != -1) {
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i);
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
|
||||
if (syslong(path) != global_max_freq)
|
||||
continue;
|
||||
}
|
||||
|
|
@ -4224,5 +4257,386 @@ void app_default_conf(char* conf, size_t conf_size, const char* argv0)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static void read_os_release(char* distro, size_t distro_size)
|
||||
{
|
||||
distro[0] = 0;
|
||||
|
||||
char buf[1024];
|
||||
ssize_t len = sysread("/etc/os-release", buf, sizeof(buf) - 1);
|
||||
if (len <= 0)
|
||||
len = sysread("/usr/lib/os-release", buf, sizeof(buf) - 1);
|
||||
if (len <= 0)
|
||||
return;
|
||||
buf[len] = 0;
|
||||
|
||||
char* p = buf;
|
||||
while ((p = strstr(p, "PRETTY_NAME=")) != 0) {
|
||||
if (p == buf || *(p - 1) == '\n')
|
||||
break;
|
||||
++p;
|
||||
}
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
p += 12;
|
||||
char q = 0;
|
||||
if (*p == '"' || *p == '\'') {
|
||||
q = *p++;
|
||||
}
|
||||
|
||||
size_t d = 0;
|
||||
while (*p != 0 && *p != '\n' && (q != 0 ? *p != q : 1)) {
|
||||
if (*p == '\\' && *(p + 1) != 0 && *(p + 1) != '\n') {
|
||||
++p;
|
||||
}
|
||||
if (d + 1 < distro_size) {
|
||||
distro[d++] = *p;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
distro[d] = 0;
|
||||
strtrim(distro);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/* machine */
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
static void format_freq(char* buf, size_t size, unsigned mhz)
|
||||
{
|
||||
if (mhz >= 1000)
|
||||
snprintf(buf, size, "%.2f GHz", mhz / 1000.0);
|
||||
else
|
||||
snprintf(buf, size, "%u MHz", mhz);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
static int sysctl_string(const char* name, char* buf, size_t size)
|
||||
{
|
||||
size_t len = size;
|
||||
|
||||
if (sysctlbyname(name, buf, &len, 0, 0) != 0) {
|
||||
buf[0] = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[size - 1] = 0;
|
||||
strtrim(buf);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void machineinfo(struct machineinfo_struct* info)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
#if defined(__x86_64__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "x86_64");
|
||||
#elif defined(__i386__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "x86_32");
|
||||
#elif defined(__aarch64__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "aarch64");
|
||||
#elif defined(__arm__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "arm");
|
||||
#elif defined(__powerpc64__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "ppc64");
|
||||
#elif defined(__powerpc__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "ppc");
|
||||
#elif defined(__riscv)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "riscv");
|
||||
#elif defined(__s390x__)
|
||||
snprintf(info->cpu_arch, sizeof(info->cpu_arch), "s390x");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
raid_cpu_info(info->cpu_vendor, &info->cpu_family, &info->cpu_model);
|
||||
|
||||
uint32_t reg[4];
|
||||
raid_cpuid(1, 0, reg);
|
||||
info->cpu_stepping = reg[0] & 0xF;
|
||||
|
||||
/* brand string from extended CPUID leaves 0x80000002..0x80000004 */
|
||||
raid_cpuid(0x80000000, 0, reg);
|
||||
if (reg[0] >= 0x80000004) {
|
||||
raid_cpuid(0x80000002, 0, reg);
|
||||
memcpy(info->cpu_brand + 0, reg, sizeof(reg));
|
||||
raid_cpuid(0x80000003, 0, reg);
|
||||
memcpy(info->cpu_brand + 16, reg, sizeof(reg));
|
||||
raid_cpuid(0x80000004, 0, reg);
|
||||
memcpy(info->cpu_brand + 32, reg, sizeof(reg));
|
||||
info->cpu_brand[48] = 0;
|
||||
strtrim(info->cpu_brand);
|
||||
}
|
||||
|
||||
/* hypervisor detection */
|
||||
raid_cpuid(1, 0, reg);
|
||||
if (reg[2] & (1U << 31)) {
|
||||
raid_cpuid(0x40000000, 0, reg);
|
||||
memcpy(info->hypervisor + 0, ®[1], 4);
|
||||
memcpy(info->hypervisor + 4, ®[2], 4);
|
||||
memcpy(info->hypervisor + 8, ®[3], 4);
|
||||
info->hypervisor[12] = 0;
|
||||
strtrim(info->hypervisor);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct utsname un;
|
||||
if (uname(&un) == 0) {
|
||||
snprintf(info->os_name, sizeof(info->os_name), "%s %s", un.sysname, un.release);
|
||||
}
|
||||
|
||||
long ps = sysconf(_SC_PAGESIZE);
|
||||
if (ps > 0)
|
||||
info->memory_page_size = (uint32_t)ps;
|
||||
|
||||
#ifdef _SC_NPROCESSORS_ONLN
|
||||
long num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (num_cpus <= 0)
|
||||
num_cpus = sysconf(_SC_NPROCESSORS_CONF);
|
||||
if (num_cpus > 0)
|
||||
info->cpu_cores_logical = (unsigned)num_cpus;
|
||||
#elif defined(_SC_NPROCESSORS_CONF)
|
||||
long num_cpus = sysconf(_SC_NPROCESSORS_CONF);
|
||||
if (num_cpus > 0)
|
||||
info->cpu_cores_logical = (unsigned)num_cpus;
|
||||
#endif
|
||||
|
||||
#ifdef _SC_LEVEL1_DCACHE_SIZE
|
||||
long l1_d = sysconf(_SC_LEVEL1_DCACHE_SIZE);
|
||||
if (l1_d > 0)
|
||||
info->cache_l1_data = (uint64_t)l1_d;
|
||||
#endif
|
||||
#ifdef _SC_LEVEL1_ICACHE_SIZE
|
||||
long l1_i = sysconf(_SC_LEVEL1_ICACHE_SIZE);
|
||||
if (l1_i > 0)
|
||||
info->cache_l1_instruction = (uint64_t)l1_i;
|
||||
#endif
|
||||
#ifdef _SC_LEVEL2_CACHE_SIZE
|
||||
long l2 = sysconf(_SC_LEVEL2_CACHE_SIZE);
|
||||
if (l2 > 0)
|
||||
info->cache_l2 = (uint64_t)l2;
|
||||
#endif
|
||||
#ifdef _SC_LEVEL3_CACHE_SIZE
|
||||
long l3 = sysconf(_SC_LEVEL3_CACHE_SIZE);
|
||||
if (l3 > 0)
|
||||
info->cache_l3 = (uint64_t)l3;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
struct sysinfo si;
|
||||
if (sysinfo(&si) == 0) {
|
||||
info->memory_total_bytes = (uint64_t)si.totalram * si.mem_unit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Group CPUs by distinct maximum frequency rather than relying on
|
||||
* consecutive ID ranges. This handles non-consecutive ID layouts cleanly
|
||||
* and avoids artificial cluster fragmentation.
|
||||
*/
|
||||
struct {
|
||||
unsigned mhz;
|
||||
unsigned count;
|
||||
unsigned cpu;
|
||||
} clusters[8];
|
||||
unsigned cluster_count = 0;
|
||||
|
||||
unsigned cpus[CPU_SETSIZE];
|
||||
unsigned online_total = read_online_cpus(cpus, sizeof(cpus) / sizeof(cpus[0]));
|
||||
unsigned scan_cpus = online_total < sizeof(cpus) / sizeof(cpus[0]) ? online_total : sizeof(cpus) / sizeof(cpus[0]);
|
||||
|
||||
if (scan_cpus > 0) {
|
||||
for (unsigned k = 0; k < scan_cpus; ++k) {
|
||||
unsigned i = cpus[k];
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
|
||||
long long khz = syslong(path);
|
||||
if (khz > 0) {
|
||||
unsigned mhz = (unsigned)(khz / 1000);
|
||||
int found = 0;
|
||||
for (unsigned c = 0; c < cluster_count; ++c) {
|
||||
if (clusters[c].mhz == mhz) {
|
||||
++clusters[c].count;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found && cluster_count < sizeof(clusters) / sizeof(clusters[0])) {
|
||||
clusters[cluster_count].mhz = mhz;
|
||||
clusters[cluster_count].count = 1;
|
||||
clusters[cluster_count].cpu = i;
|
||||
++cluster_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* sort clusters by frequency descending (fastest cores first) */
|
||||
for (unsigned i = 0; i < cluster_count; ++i) {
|
||||
for (unsigned j = i + 1; j < cluster_count; ++j) {
|
||||
if (clusters[j].mhz > clusters[i].mhz) {
|
||||
unsigned tmp_mhz = clusters[i].mhz;
|
||||
unsigned tmp_count = clusters[i].count;
|
||||
unsigned tmp_cpu = clusters[i].cpu;
|
||||
clusters[i].mhz = clusters[j].mhz;
|
||||
clusters[i].count = clusters[j].count;
|
||||
clusters[i].cpu = clusters[j].cpu;
|
||||
clusters[j].mhz = tmp_mhz;
|
||||
clusters[j].count = tmp_count;
|
||||
clusters[j].cpu = tmp_cpu;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cluster_count > 1) {
|
||||
info->cpu_clock[0] = 0;
|
||||
size_t pos = 0;
|
||||
for (unsigned c = 0; c < cluster_count; ++c) {
|
||||
char freq_str[32];
|
||||
format_freq(freq_str, sizeof(freq_str), clusters[c].mhz);
|
||||
char part[64];
|
||||
if (clusters[c].count == 1)
|
||||
snprintf(part, sizeof(part), "%s%s (1 cpu)", c > 0 ? ", " : "", freq_str);
|
||||
else
|
||||
snprintf(part, sizeof(part), "%s%s (%u cpus)", c > 0 ? ", " : "", freq_str, clusters[c].count);
|
||||
if (pos < sizeof(info->cpu_clock)) {
|
||||
int n = snprintf(info->cpu_clock + pos, sizeof(info->cpu_clock) - pos, "%s", part);
|
||||
if (n > 0)
|
||||
pos += (size_t)n;
|
||||
}
|
||||
}
|
||||
} else if (cluster_count == 1) {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/cpufreq/base_frequency", clusters[0].cpu);
|
||||
long long base_khz = syslong(path);
|
||||
unsigned base_mhz = base_khz > 0 ? (unsigned)(base_khz / 1000) : 0;
|
||||
unsigned max_mhz = clusters[0].mhz;
|
||||
char max_str[32];
|
||||
char base_str[32];
|
||||
format_freq(max_str, sizeof(max_str), max_mhz);
|
||||
if (base_mhz > 0 && base_mhz != max_mhz) {
|
||||
format_freq(base_str, sizeof(base_str), base_mhz);
|
||||
snprintf(info->cpu_clock, sizeof(info->cpu_clock), "%s (max %s)", base_str, max_str);
|
||||
} else {
|
||||
snprintf(info->cpu_clock, sizeof(info->cpu_clock), "%s", max_str);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback if sysfs cpufreq is unavailable (e.g. Cloud VM) */
|
||||
if (info->cpu_clock[0] == 0) {
|
||||
#ifdef CONFIG_X86
|
||||
raid_cpuid(0, 0, reg);
|
||||
if (reg[0] >= 0x16) {
|
||||
raid_cpuid(0x16, 0, reg);
|
||||
unsigned base_mhz = reg[0] & 0xFFFF;
|
||||
unsigned max_mhz = reg[1] & 0xFFFF;
|
||||
if (base_mhz > 0 && max_mhz > 0 && base_mhz != max_mhz) {
|
||||
char base_str[32], max_str[32];
|
||||
format_freq(base_str, sizeof(base_str), base_mhz);
|
||||
format_freq(max_str, sizeof(max_str), max_mhz);
|
||||
snprintf(info->cpu_clock, sizeof(info->cpu_clock), "%s (max %s)", base_str, max_str);
|
||||
} else if (base_mhz > 0) {
|
||||
format_freq(info->cpu_clock, sizeof(info->cpu_clock), base_mhz);
|
||||
} else if (max_mhz > 0) {
|
||||
format_freq(info->cpu_clock, sizeof(info->cpu_clock), max_mhz);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (info->cpu_clock[0] == 0) {
|
||||
char buf[1024];
|
||||
ssize_t len = sysread("/proc/cpuinfo", buf, sizeof(buf) - 1);
|
||||
if (len > 0) {
|
||||
buf[len] = 0;
|
||||
char* p = strstr(buf, "cpu MHz");
|
||||
if (p) {
|
||||
p = strchr(p, ':');
|
||||
if (p) {
|
||||
double mhz = strtod(p + 1, 0);
|
||||
if (mhz > 0)
|
||||
format_freq(info->cpu_clock, sizeof(info->cpu_clock), (unsigned)(mhz + 0.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char model_buf[128];
|
||||
if (sysattr("/sys/class/dmi/id/product_name", model_buf, sizeof(model_buf)) == 0 && model_buf[0]) {
|
||||
char vendor_buf[128];
|
||||
if (sysattr("/sys/class/dmi/id/sys_vendor", vendor_buf, sizeof(vendor_buf)) == 0 && vendor_buf[0]) {
|
||||
snprintf(info->system_model, sizeof(info->system_model), "%s %s", vendor_buf, model_buf);
|
||||
} else {
|
||||
snprintf(info->system_model, sizeof(info->system_model), "%s", model_buf);
|
||||
}
|
||||
} else if (sysattr("/proc/device-tree/model", model_buf, sizeof(model_buf)) == 0 && model_buf[0]) {
|
||||
snprintf(info->system_model, sizeof(info->system_model), "%s", model_buf);
|
||||
}
|
||||
|
||||
read_os_release(info->os_distribution, sizeof(info->os_distribution));
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
size_t len;
|
||||
|
||||
if (info->cpu_brand[0] == 0)
|
||||
sysctl_string("machdep.cpu.brand_string", info->cpu_brand, sizeof(info->cpu_brand));
|
||||
|
||||
sysctl_string("hw.model", info->system_model, sizeof(info->system_model));
|
||||
|
||||
unsigned val = 0;
|
||||
len = sizeof(val);
|
||||
if (sysctlbyname("hw.physicalcpu", &val, &len, 0, 0) == 0)
|
||||
info->cpu_cores_physical = val;
|
||||
|
||||
len = sizeof(val);
|
||||
if (sysctlbyname("hw.logicalcpu", &val, &len, 0, 0) == 0)
|
||||
info->cpu_cores_logical = val;
|
||||
|
||||
uint64_t hz = 0;
|
||||
len = sizeof(hz);
|
||||
if (sysctlbyname("hw.cpufrequency", &hz, &len, 0, 0) == 0 && hz > 0)
|
||||
format_freq(info->cpu_clock, sizeof(info->cpu_clock), (unsigned)(hz / 1000000));
|
||||
|
||||
unsigned long clen = 0;
|
||||
len = sizeof(clen);
|
||||
if (sysctlbyname("hw.l1dcachesize", &clen, &len, 0, 0) == 0)
|
||||
info->cache_l1_data = clen;
|
||||
|
||||
len = sizeof(clen);
|
||||
if (sysctlbyname("hw.l1icachesize", &clen, &len, 0, 0) == 0)
|
||||
info->cache_l1_instruction = clen;
|
||||
|
||||
len = sizeof(clen);
|
||||
if (sysctlbyname("hw.l2cachesize", &clen, &len, 0, 0) == 0)
|
||||
info->cache_l2 = clen;
|
||||
|
||||
len = sizeof(clen);
|
||||
if (sysctlbyname("hw.l3cachesize", &clen, &len, 0, 0) == 0)
|
||||
info->cache_l3 = clen;
|
||||
|
||||
uint64_t mem = 0;
|
||||
len = sizeof(mem);
|
||||
if (sysctlbyname("hw.memsize", &mem, &len, 0, 0) == 0)
|
||||
info->memory_total_bytes = mem;
|
||||
|
||||
char osver[32];
|
||||
sysctl_string("kern.osproductversion", osver, sizeof(osver));
|
||||
|
||||
char osrel[32];
|
||||
sysctl_string("kern.osrelease", osrel, sizeof(osrel));
|
||||
|
||||
if (osver[0] && osrel[0]) {
|
||||
snprintf(info->os_name, sizeof(info->os_name), "macOS %s (Darwin %s)", osver, osrel);
|
||||
} else if (osver[0]) {
|
||||
snprintf(info->os_name, sizeof(info->os_name), "macOS %s", osver);
|
||||
} else if (osrel[0]) {
|
||||
snprintf(info->os_name, sizeof(info->os_name), "Darwin %s", osrel);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue