Bug 1128472 - Part 2. Linux support. r=gfritzsche
authorMilan Sreckovic <milan@mozilla.com>
Tue, 08 Sep 2015 14:35:00 +0200
changeset 294110 912d328e8ac0e94dc97584900856bf19446bd94e
parent 294109 95b43173a47b29d25dfb08f76676adcf30cc00a0
child 294111 6d67e5c0e73b8f8e5c55d357e2b3012a417f62fc
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [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 2. Linux support. r=gfritzsche
toolkit/components/telemetry/docs/environment.rst
toolkit/components/telemetry/tests/unit/head.js
toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
xpcom/base/nsSystemInfo.cpp
--- a/toolkit/components/telemetry/docs/environment.rst
+++ b/toolkit/components/telemetry/docs/environment.rst
@@ -73,25 +73,25 @@ Structure::
           // 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 - 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
+            count: <number>,  // desktop only, e.g. 8, or null on failure - logical cpus
+            cores: <number>, // desktop only, e.g., 4, or null on failure - physical cores
+            vendor: <string>, // e.g. "GenuineIntel", or null on failure, only on mac & linux
+            family: <string>, // desktop only, null on failure
+            model: <string>, // desktop only, null on failure
+            stepping: <string>, // desktop only, null on failure
             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
+            l3cacheKB: <number>, // desktop only, L3 cache size in KB
+            speedMHz: <number>, // desktop only, cpu clock speed in MHz
             extensions: [
               <string>,
               ...
               // as applicable:
               // "MMX", "SSE", "SSE2", "SSE3", "SSSE3", "SSE4A", "SSE4_1",
               // "SSE4_2", "EDSP", "ARMv6", "ARMv7", "NEON"
             ],
         },
--- a/toolkit/components/telemetry/tests/unit/head.js
+++ b/toolkit/components/telemetry/tests/unit/head.js
@@ -3,21 +3,23 @@
 
 const { classes: Cc, utils: Cu, interfaces: Ci, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/TelemetryController.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
 Cu.import("resource://testing-common/httpd.js", this);
+Cu.import("resource://gre/modules/AppConstants.jsm");
 
-const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
-const gIsMac = ("@mozilla.org/xpcom/mac-utils;1" in Cc);
-const gIsAndroid =  ("@mozilla.org/android/bridge;1" in Cc);
-const gIsGonk = ("@mozilla.org/cellbroadcast/gonkservice;1" in Cc);
+const gIsWindows = AppConstants.platform == "win";
+const gIsMac = AppConstants.platform == "macosx";
+const gIsAndroid = AppConstants.platform == "android";
+const gIsGonk = AppConstants.platform == "gonk";
+const gIsLinux = AppConstants.platform == "linux";
 
 const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
 
 const MILLISECONDS_PER_MINUTE = 60 * 1000;
 const MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE;
 const MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR;
 
 const HAS_DATAREPORTINGSERVICE = "@mozilla.org/datareporting/service;1" in Cc;
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -361,20 +361,20 @@ function checkSystemSection(data) {
 
   // 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 || gIsMac) {
+  if (gIsWindows || gIsMac || gIsLinux) {
     let EXTRA_CPU_FIELDS = ["cores", "model", "family", "stepping",
 			    "l2cacheKB", "l3cacheKB", "speedMHz"];
-    if (gIsMac) {
+    if (gIsMac || gIsLinux) {
       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.");
     }
@@ -383,24 +383,25 @@ function checkSystemSection(data) {
       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"]) {
+    for (let f of ["cores"]) {
 	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"]) {
+    for (let f of ["model", "family", "stepping", "l2cacheKB",
+		   "l3cacheKB", "speedMHz"]) {
 	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;
--- a/xpcom/base/nsSystemInfo.cpp
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -26,16 +26,23 @@
 #endif
 
 #ifdef XP_MACOSX
 #include "MacHelpers.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK
 #include <gtk/gtk.h>
+#include <unistd.h>
+#include <fstream>
+#include "mozilla/Tokenizer.h"
+#include "nsCharSeparatedTokenizer.h"
+
+#include <map>
+#include <string>
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include <sys/system_properties.h>
@@ -59,16 +66,40 @@ NS_EXPORT int android_sdk_version;
 
 // 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
 // only happens well after that point.
 uint32_t nsSystemInfo::gUserUmask = 0;
 
+#if defined (MOZ_WIDGET_GTK)
+static void
+SimpleParseKeyValuePairs(const std::string& aFilename,
+                         std::map<nsCString, nsCString>& aKeyValuePairs)
+{
+  std::ifstream input(aFilename.c_str());
+  for (std::string line; std::getline(input, line); ) {
+    nsAutoCString key, value;
+
+    nsCCharSeparatedTokenizer tokens(nsDependentCString(line.c_str()), ':');
+    if (tokens.hasMoreTokens()) {
+      key = tokens.nextToken();
+      if (tokens.hasMoreTokens()) {
+        value = tokens.nextToken();
+      }
+      // We want the value even if there was just one token, to cover the
+      // case where we had the key, and the value was blank (seems to be
+      // a valid scenario some files.)
+      aKeyValuePairs[key] = value;
+    }
+  }
+}
+#endif
+
 #if defined(XP_WIN)
 namespace {
 nsresult
 GetHDDInfo(const char* aSpecialDirName, nsAutoCString& aModel,
            nsAutoCString& aRevision)
 {
   aModel.Truncate();
   aRevision.Truncate();
@@ -442,16 +473,113 @@ nsSystemInfo::Init()
   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);
 
+#elif defined (MOZ_WIDGET_GTK)
+  // Get vendor, family, model, stepping, physical cores, L3 cache size
+  // from /proc/cpuinfo file
+  {
+    std::map<nsCString, nsCString> keyValuePairs;
+    SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
+
+    // cpuVendor from "vendor_id"
+    cpuVendor.Assign(keyValuePairs[NS_LITERAL_CSTRING("vendor_id")]);
+
+    {
+      // cpuFamily from "cpu family"
+      Tokenizer::Token t;
+      Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu family")]);
+      if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+          t.AsInteger() <= INT32_MAX) {
+        cpuFamily = static_cast<int>(t.AsInteger());
+      }
+    }
+
+    {
+      // cpuModel from "model"
+      Tokenizer::Token t;
+      Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("model")]);
+      if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+          t.AsInteger() <= INT32_MAX) {
+        cpuModel = static_cast<int>(t.AsInteger());
+      }
+    }
+
+    {
+      // cpuStepping from "stepping"
+      Tokenizer::Token t;
+      Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("stepping")]);
+      if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+          t.AsInteger() <= INT32_MAX) {
+        cpuStepping = static_cast<int>(t.AsInteger());
+      }
+    }
+
+    {
+      // physicalCPUs from "cpu cores"
+      Tokenizer::Token t;
+      Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu cores")]);
+      if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+          t.AsInteger() <= INT32_MAX) {
+        physicalCPUs = static_cast<int>(t.AsInteger());
+      }
+    }
+
+    {
+      // cacheSizeL3 from "cache size"
+      Tokenizer::Token t;
+      Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cache size")]);
+      if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+          t.AsInteger() <= INT32_MAX) {
+        cacheSizeL3 = static_cast<int>(t.AsInteger());
+        if (p.Next(t) && t.Type() == Tokenizer::TOKEN_WORD &&
+            t.AsString() != NS_LITERAL_CSTRING("KB")) {
+          // If we get here, there was some text after the cache size value
+          // and that text was not KB.  For now, just don't report the
+          // L3 cache.
+          cacheSizeL3 = -1;
+        }
+      }
+    }
+  }
+
+  {
+    // Get cpuSpeed from another file.
+    std::ifstream input("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
+    std::string line;
+    if (getline(input, line)) {
+      Tokenizer::Token t;
+      Tokenizer p(line.c_str());
+      if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+          t.AsInteger() <= INT32_MAX) {
+        cpuSpeed = static_cast<int>(t.AsInteger()/1000);
+      }
+    }
+  }
+
+  {
+    // Get cacheSizeL2 from yet another file
+    std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index2/size");
+    std::string line;
+    if (getline(input, line)) {
+      Tokenizer::Token t;
+      Tokenizer p(line.c_str(), nullptr, "K");
+      if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+          t.AsInteger() <= INT32_MAX) {
+        cacheSizeL2 = static_cast<int>(t.AsInteger());
+      }
+    }
+  }
+
+  SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
 #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);