From c2e36204f080bab32d0de7750e58ffc23f39d33d Mon Sep 17 00:00:00 2001 From: Milan Sreckovic Date: Tue, 1 Sep 2015 14:48:00 +0200 Subject: [PATCH] Bug 1128472 - Part 1. Mac and Win for model, stepping, cores, cache, cpu speed; VM max on Win only, vendor on Mac only. r=gfritzsche --- .../telemetry/TelemetryEnvironment.jsm | 20 +- .../components/telemetry/docs/environment.rst | 15 +- .../tests/unit/test_TelemetryEnvironment.js | 39 +++- xpcom/base/nsSystemInfo.cpp | 190 +++++++++++++++++- 4 files changed, 251 insertions(+), 13 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryEnvironment.jsm b/toolkit/components/telemetry/TelemetryEnvironment.jsm index 950d18ab1169..8a395f55656f 100644 --- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -1077,10 +1077,14 @@ EnvironmentCache.prototype = { _getCpuData: function () { let cpuData = { count: getSysinfoProperty("cpucount", null), - vendor: null, // TODO: bug 1128472 - family: null, // TODO: bug 1128472 - model: null, // TODO: bug 1128472 - stepping: null, // TODO: bug 1128472 + cores: getSysinfoProperty("cpucores", null), + vendor: getSysinfoProperty("cpuvendor", null), + family: getSysinfoProperty("cpufamily", null), + model: getSysinfoProperty("cpumodel", null), + stepping: getSysinfoProperty("cpustepping", null), + l2cacheKB: getSysinfoProperty("cpucachel2", null), + l3cacheKB: getSysinfoProperty("cpucachel3", null), + speedMHz: getSysinfoProperty("cpuspeed", null), }; const CPU_EXTENSIONS = ["hasMMX", "hasSSE", "hasSSE2", "hasSSE3", "hasSSSE3", @@ -1223,8 +1227,16 @@ EnvironmentCache.prototype = { memoryMB = Math.round(memoryMB / 1024 / 1024); } + let virtualMB = getSysinfoProperty("virtualmemsize", null); + if (virtualMB) { + // Send the total virtual memory size in megabytes. Rounding because + // sysinfo doesn't always provide RAM in multiples of 1024. + virtualMB = Math.round(virtualMB / 1024 / 1024); + } + return { memoryMB: memoryMB, + virtualMaxMB: virtualMB, #ifdef XP_WIN isWow64: getSysinfoProperty("isWow64", null), #endif diff --git a/toolkit/components/telemetry/docs/environment.rst b/toolkit/components/telemetry/docs/environment.rst index 2f6a426005a1..f22d935db037 100644 --- a/toolkit/components/telemetry/docs/environment.rst +++ b/toolkit/components/telemetry/docs/environment.rst @@ -75,13 +75,18 @@ Structure:: }, system: { memoryMB: , + virtualMaxMB: , // windows-only isWow64: , // windows-only cpu: { - count: , // e.g. 8, or null on failure - vendor: , // e.g. "GenuineIntel", or null on failure - family: , // null on failure - model: , // null on failure - stepping: , // null on failure + count: , // e.g. 8, or null on failure - logical cpus + cores: , // e.g., 4, or null on failure - physical cores, only on windows & mac + vendor: , // e.g. "GenuineIntel", or null on failure, only on mac + family: , // null on failure, only on windows & mac + model: , // null on failure, only on windows & mac + stepping: , // null on failure, only on windows & mac + l2cacheKB: , // L2 cache size in KB, only on windows & mac + l3cacheKB: , // L3 cache size in KB, only on windows & mac + speedMHz: , // cpu clock speed in MHz, only on windows & mac extensions: [ , ... diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js index 1f128be650f4..82ecfb4e567b 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js @@ -365,9 +365,42 @@ function checkSystemSection(data) { } Assert.ok(Number.isFinite(data.system.memoryMB), "MemoryMB must be a number."); - if (gIsWindows) { - Assert.equal(typeof data.system.isWow64, "boolean", - "isWow64 must be available on Windows and have the correct type."); + + if (gIsWindows || gIsMac) { + let EXTRA_CPU_FIELDS = ["cores", "model", "family", "stepping", + "l2cacheKB", "l3cacheKB", "speedMHz"]; + if (gIsMac) { + EXTRA_CPU_FIELDS.push("vendor"); + } + + for (let f of EXTRA_CPU_FIELDS) { + // Note this is testing TelemetryEnvironment.js only, not that the + // values are valid - null is the fallback. + Assert.ok(f in data.system.cpu, f + " must be available under cpu."); + } + + if (gIsWindows) { + Assert.equal(typeof data.system.isWow64, "boolean", + "isWow64 must be available on Windows and have the correct type."); + Assert.ok("virtualMaxMB" in data.system, "virtualMaxMB must be available."); + Assert.ok(Number.isFinite(data.system.virtualMaxMB), + "virtualMaxMB must be a number."); + } + + // We insist these are available + for (let f of ["cores", "speedMHz"]) { + Assert.ok(!(f in data.system.cpu) || + Number.isFinite(data.system.cpu[f]), + f + " must be a number if non null."); + } + + // These should be numbers if they are not null + for (let f of ["model", "family", "stepping", "l2cacheKB", "l3cacheKB"]) { + Assert.ok(!(f in data.system.cpu) || + data.system.cpu[f] === null || + Number.isFinite(data.system.cpu[f]), + f + " must be a number if non null."); + } } let cpuData = data.system.cpu; diff --git a/xpcom/base/nsSystemInfo.cpp b/xpcom/base/nsSystemInfo.cpp index acbc6defc015..84bfb2026b7f 100644 --- a/xpcom/base/nsSystemInfo.cpp +++ b/xpcom/base/nsSystemInfo.cpp @@ -49,6 +49,10 @@ NS_EXPORT int android_sdk_version; } #endif +#ifdef XP_MACOSX +#include +#endif + #if defined(XP_LINUX) && defined(MOZ_SANDBOX) #include "mozilla/SandboxInfo.h" #endif @@ -235,6 +239,66 @@ static const struct PropItems { "hasNEON", mozilla::supports_neon } }; +#ifdef XP_WIN +// Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc, +// so keeping the _ instead of switching to camel case for now. +typedef BOOL (WINAPI *LPFN_GLPI)( + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, + PDWORD); +static void +GetProcessorInformation(int* physical_cpus, int* cache_size_L2, int* cache_size_L3) +{ + MOZ_ASSERT(physical_cpus && cache_size_L2 && cache_size_L3); + + *physical_cpus = 0; + *cache_size_L2 = 0; // This will be in kbytes + *cache_size_L3 = 0; // This will be in kbytes + + // GetLogicalProcessorInformation() is available on Windows XP SP3 and beyond. + LPFN_GLPI glpi = reinterpret_cast(GetProcAddress( + GetModuleHandle(L"kernel32"), + "GetLogicalProcessorInformation")); + if (nullptr == glpi) { + return; + } + // Determine buffer size, allocate and get processor information. + // Size can change between calls (unlikely), so a loop is done. + SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer[32]; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION* infos = &info_buffer[0]; + DWORD return_length = sizeof(info_buffer); + while (!glpi(infos, &return_length)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && infos == &info_buffer[0]) { + infos = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)]; + } else { + return; + } + } + + for (size_t i = 0; + i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) { + if (infos[i].Relationship == RelationProcessorCore) { + ++*physical_cpus; + } else if (infos[i].Relationship == RelationCache) { + // Only care about L2 and L3 cache + switch (infos[i].Cache.Level) { + case 2: + *cache_size_L2 = static_cast(infos[i].Cache.Size/1024); + break; + case 3: + *cache_size_L3 = static_cast(infos[i].Cache.Size/1024); + break; + default: + break; + } + } + } + if (infos != &info_buffer[0]) { + delete [] infos; + } + return; +} +#endif + nsresult nsSystemInfo::Init() { @@ -272,10 +336,134 @@ nsSystemInfo::Init() SetInt32Property(NS_LITERAL_STRING("pagesize"), PR_GetPageSize()); SetInt32Property(NS_LITERAL_STRING("pageshift"), PR_GetPageShift()); SetInt32Property(NS_LITERAL_STRING("memmapalign"), PR_GetMemMapAlignment()); - SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors()); SetUint64Property(NS_LITERAL_STRING("memsize"), PR_GetPhysicalMemorySize()); SetUint32Property(NS_LITERAL_STRING("umask"), nsSystemInfo::gUserUmask); + uint64_t virtualMem = 0; + nsAutoCString cpuVendor; + int cpuSpeed = -1; + int cpuFamily = -1; + int cpuModel = -1; + int cpuStepping = -1; + int logicalCPUs = -1; + int physicalCPUs = -1; + int cacheSizeL2 = -1; + int cacheSizeL3 = -1; + +#if defined (XP_WIN) + // Virtual memory: + MEMORYSTATUSEX memStat; + memStat.dwLength = sizeof(memStat); + if (GlobalMemoryStatusEx(&memStat)) { + virtualMem = memStat.ullTotalVirtual; + } + + // CPU speed + HKEY key; + static const WCHAR keyName[] = + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key) + == ERROR_SUCCESS) { + DWORD data, len; + len = sizeof(data); + + if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast(&data), + &len) == ERROR_SUCCESS) { + cpuSpeed = static_cast(data); + } + RegCloseKey(key); + } + + // Other CPU attributes: + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + logicalCPUs = si.dwNumberOfProcessors; + GetProcessorInformation(&physicalCPUs, &cacheSizeL2, &cacheSizeL3); + if (physicalCPUs <= 0) { + physicalCPUs = logicalCPUs; + } + cpuFamily = si.wProcessorLevel; + cpuModel = si.wProcessorRevision >> 8; + cpuStepping = si.wProcessorRevision & 0xFF; +#elif defined (XP_MACOSX) + // CPU speed + uint64_t sysctlValue64 = 0; + uint32_t sysctlValue32 = 0; + size_t len = 0; + len = sizeof(sysctlValue64); + if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64, &len, NULL, 0)) { + cpuSpeed = static_cast(sysctlValue64/1000000); + } + MOZ_ASSERT(sizeof(sysctlValue64) == len); + + len = sizeof(sysctlValue32); + if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32, &len, NULL, 0)) { + physicalCPUs = static_cast(sysctlValue32); + } + MOZ_ASSERT(sizeof(sysctlValue32) == len); + + len = sizeof(sysctlValue32); + if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32, &len, NULL, 0)) { + logicalCPUs = static_cast(sysctlValue32); + } + MOZ_ASSERT(sizeof(sysctlValue32) == len); + + len = sizeof(sysctlValue64); + if (!sysctlbyname("hw.l2cachesize", &sysctlValue64, &len, NULL, 0)) { + cacheSizeL2 = static_cast(sysctlValue64/1024); + } + MOZ_ASSERT(sizeof(sysctlValue64) == len); + + len = sizeof(sysctlValue64); + if (!sysctlbyname("hw.l3cachesize", &sysctlValue64, &len, NULL, 0)) { + cacheSizeL3 = static_cast(sysctlValue64/1024); + } + MOZ_ASSERT(sizeof(sysctlValue64) == len); + + if (!sysctlbyname("machdep.cpu.vendor", NULL, &len, NULL, 0)) { + char* cpuVendorStr = new char[len]; + if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr, &len, NULL, 0)) { + cpuVendor = cpuVendorStr; + } + delete [] cpuVendorStr; + } + + len = sizeof(sysctlValue32); + if (!sysctlbyname("machdep.cpu.family", &sysctlValue32, &len, NULL, 0)) { + cpuFamily = static_cast(sysctlValue32); + } + MOZ_ASSERT(sizeof(sysctlValue32) == len); + + len = sizeof(sysctlValue32); + if (!sysctlbyname("machdep.cpu.model", &sysctlValue32, &len, NULL, 0)) { + cpuModel = static_cast(sysctlValue32); + } + MOZ_ASSERT(sizeof(sysctlValue32) == len); + + len = sizeof(sysctlValue32); + if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) { + cpuStepping = static_cast(sysctlValue32); + } + MOZ_ASSERT(sizeof(sysctlValue32) == len); + +#else + SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors()); +#endif + + if (virtualMem) SetUint64Property(NS_LITERAL_STRING("virtualmemsize"), virtualMem); + if (cpuSpeed >= 0) SetInt32Property(NS_LITERAL_STRING("cpuspeed"), cpuSpeed); + if (!cpuVendor.IsEmpty()) SetPropertyAsACString(NS_LITERAL_STRING("cpuvendor"), cpuVendor); + if (cpuFamily >= 0) SetInt32Property(NS_LITERAL_STRING("cpufamily"), cpuFamily); + if (cpuModel >= 0) SetInt32Property(NS_LITERAL_STRING("cpumodel"), cpuModel); + if (cpuStepping >= 0) SetInt32Property(NS_LITERAL_STRING("cpustepping"), cpuStepping); + + if (logicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucount"), logicalCPUs); + if (physicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucores"), physicalCPUs); + + if (cacheSizeL2 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel2"), cacheSizeL2); + if (cacheSizeL3 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel3"), cacheSizeL3); + for (uint32_t i = 0; i < ArrayLength(cpuPropItems); i++) { rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems[i].name), cpuPropItems[i].propfun());