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
authorMilan Sreckovic <msreckovic@mozilla.com>
Tue, 01 Sep 2015 14:48:00 +0200
changeset 261510 95b43173a47b29d25dfb08f76676adcf30cc00a0
parent 261401 982e7fa091fa6ddd9cd48555228d614209099515
child 261511 912d328e8ac0e94dc97584900856bf19446bd94e
push id29347
push userkwierso@gmail.com
push dateWed, 09 Sep 2015 20:43:09 +0000
treeherdermozilla-central@7c8e40e00bfe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfritzsche
bugs1128472
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
toolkit/components/telemetry/TelemetryEnvironment.jsm
toolkit/components/telemetry/docs/environment.rst
toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
xpcom/base/nsSystemInfo.cpp
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -1072,20 +1072,24 @@ EnvironmentCache.prototype = {
 
   /**
    * Get the CPU information.
    * @return Object containing the CPU information data.
    */
   _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",
                             "hasSSE4A", "hasSSE4_1", "hasSSE4_2", "hasEDSP", "hasARMv6",
                             "hasARMv7", "hasNEON"];
 
     // Enumerate the available CPU extensions.
     let availableExts = [];
@@ -1218,18 +1222,26 @@ EnvironmentCache.prototype = {
   _getSystem: function () {
     let memoryMB = getSysinfoProperty("memsize", null);
     if (memoryMB) {
       // Send RAM size in megabytes. Rounding because sysinfo doesn't
       // always provide RAM in multiples of 1024.
       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
       cpu: this._getCpuData(),
 #if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
       device: this._getDeviceData(),
 #endif
       os: this._getOSData(),
--- a/toolkit/components/telemetry/docs/environment.rst
+++ b/toolkit/components/telemetry/docs/environment.rst
@@ -70,23 +70,28 @@ Structure::
         distributor: <string>, // pref app.distributor, null on failure
         distributorChannel: <string>, // pref app.distributor.channel, null on failure
         partnerNames: [
           // list from prefs app.partner.<name>=<name>
         ],
       },
       system: {
         memoryMB: <number>,
+        virtualMaxMB: <number>, // windows-only
         isWow64: <bool>, // windows-only
         cpu: {
-            count: <number>,  // e.g. 8, or null on failure
-            vendor: <string>, // e.g. "GenuineIntel", or null on failure
-            family: <string>, // null on failure
-            model: <string>, // null on failure
-            stepping: <string>, // null on failure
+            count: <number>,  // e.g. 8, or null on failure - logical cpus
+            cores: <number>, // e.g., 4, or null on failure - physical cores, only on windows & mac
+            vendor: <string>, // e.g. "GenuineIntel", or null on failure, only on mac
+            family: <string>, // null on failure, only on windows & mac
+            model: <string>, // null on failure, only on windows & mac
+            stepping: <string>, // null on failure, only on windows & mac
+            l2cacheKB: <number>, // L2 cache size in KB, only on windows & mac
+            l3cacheKB: <number>, // L3 cache size in KB, only on windows & mac
+            speedMHz: <number>, // cpu clock speed in MHz, only on windows & mac
             extensions: [
               <string>,
               ...
               // as applicable:
               // "MMX", "SSE", "SSE2", "SSE3", "SSSE3", "SSE4A", "SSE4_1",
               // "SSE4_2", "EDSP", "ARMv6", "ARMv7", "NEON"
             ],
         },
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -360,19 +360,52 @@ function checkSystemSection(data) {
   Assert.ok("system" in data, "There must be a system section in Environment.");
 
   // Make sure we have all the top level sections and fields.
   for (let f of EXPECTED_FIELDS) {
     Assert.ok(f in data.system, f + " must be available.");
   }
 
   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;
   Assert.ok(Number.isFinite(cpuData.count), "CPU count must be a number.");
   Assert.ok(Array.isArray(cpuData.extensions), "CPU extensions must be available.");
 
   // Device data is only available on Android or Gonk.
   if (gIsAndroid || gIsGonk) {
--- a/xpcom/base/nsSystemInfo.cpp
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -44,16 +44,20 @@
 #endif
 
 #ifdef ANDROID
 extern "C" {
 NS_EXPORT int android_sdk_version;
 }
 #endif
 
+#ifdef XP_MACOSX
+#include <sys/sysctl.h>
+#endif
+
 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
 #include "mozilla/SandboxInfo.h"
 #endif
 
 // Slot for NS_InitXPCOM2 to pass information to nsSystemInfo::Init.
 // Only set to nonzero (potentially) if XP_UNIX.  On such systems, the
 // system call to discover the appropriate value is not thread-safe,
 // so we must call it before going multithreaded, but nsSystemInfo::Init
@@ -230,16 +234,76 @@ static const struct PropItems
   { "hasSSE4_2", mozilla::supports_sse4_2 },
   // ARM-specific bits.
   { "hasEDSP", mozilla::supports_edsp },
   { "hasARMv6", mozilla::supports_armv6 },
   { "hasARMv7", mozilla::supports_armv7 },
   { "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<LPFN_GLPI>(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<int>(infos[i].Cache.Size/1024);
+          break;
+        case 3:
+          *cache_size_L3 = static_cast<int>(infos[i].Cache.Size/1024);
+          break;
+        default:
+          break;
+      }
+    }
+  }
+  if (infos != &info_buffer[0]) {
+    delete [] infos;
+  }
+  return;
+}
+#endif
+
 nsresult
 nsSystemInfo::Init()
 {
   nsresult rv;
 
   static const struct
   {
     PRSysInfo cmd;
@@ -267,20 +331,144 @@ nsSystemInfo::Init()
   rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16("hasWindowsTouchInterface"),
                          false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Additional informations not available through PR_GetSystemInfo.
   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<LPBYTE>(&data),
+                        &len) == ERROR_SUCCESS) {
+      cpuSpeed = static_cast<int>(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<int>(sysctlValue64/1000000);
+  }
+  MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+  len = sizeof(sysctlValue32);
+  if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
+    physicalCPUs = static_cast<int>(sysctlValue32);
+  }
+  MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+  len = sizeof(sysctlValue32);
+  if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
+    logicalCPUs = static_cast<int>(sysctlValue32);
+  }
+  MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+  len = sizeof(sysctlValue64);
+  if (!sysctlbyname("hw.l2cachesize", &sysctlValue64, &len, NULL, 0)) {
+    cacheSizeL2 = static_cast<int>(sysctlValue64/1024);
+  }
+  MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+  len = sizeof(sysctlValue64);
+  if (!sysctlbyname("hw.l3cachesize", &sysctlValue64, &len, NULL, 0)) {
+    cacheSizeL3 = static_cast<int>(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<int>(sysctlValue32);
+  }
+  MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+  len = sizeof(sysctlValue32);
+  if (!sysctlbyname("machdep.cpu.model", &sysctlValue32, &len, NULL, 0)) {
+    cpuModel = static_cast<int>(sysctlValue32);
+  }
+  MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+  len = sizeof(sysctlValue32);
+  if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) {
+    cpuStepping = static_cast<int>(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());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }