Include enabled addons + persona in telemetry r=Mossop
authorTaras Glek <tglek@mozilla.com>
Mon, 24 Oct 2011 16:04:04 -0700
changeset 79158 f24bb32f3103af157c347660acabdc4be8c01a20
parent 79157 10b1706610031fd0bae830bd0b2fef9170653f17
child 79159 c81d0114767361b94e7f28c00bef444d0a75a9b5
child 79957 27ecabf7e55e9a9b5d9a0900e275fb50ef14e9d3
push id2885
push usertglek@mozilla.com
push dateMon, 24 Oct 2011 23:04:30 +0000
treeherdermozilla-inbound@f24bb32f3103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMossop
milestone10.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
Include enabled addons + persona in telemetry r=Mossop
toolkit/components/telemetry/TelemetryPing.js
toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
toolkit/mozapps/extensions/XPIProvider.jsm
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -35,16 +35,17 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
 
 // When modifying the payload in incompatible ways, please bump this version number
 const PAYLOAD_VERSION = 1;
 
 const PREF_SERVER = "toolkit.telemetry.server";
 const PREF_ENABLED = "toolkit.telemetry.enabled";
 // Do not gather data more than once a minute
 const TELEMETRY_INTERVAL = 60000;
@@ -226,16 +227,64 @@ TelemetryPing.prototype = {
     if (!h) {
       h = Telemetry.getHistogramById(id);
       this._histograms[name] = h;
     }
     h.add(val);
   },
 
   /**
+   * Descriptive metadata
+   * 
+   * @param  reason
+   *         The reason for the telemetry ping, this will be included in the
+   *         returned metadata,
+   * @return The metadata as a JS object
+   */
+  getMetadata: function getMetadata(reason) {
+    let ai = Services.appinfo;
+    let ret = {
+      reason: reason,
+      OS: ai.OS,
+      appID: ai.ID,
+      appVersion: ai.version,
+      appName: ai.name,
+      appBuildID: ai.appBuildID,
+      platformBuildID: ai.platformBuildID,
+    };
+
+    // sysinfo fields are not always available, get what we can.
+    let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
+    let fields = ["cpucount", "memsize", "arch", "version", "device", "manufacturer", "hardware"];
+    for each (let field in fields) {
+      let value;
+      try {
+        value = sysInfo.getProperty(field);
+      } catch (e) {
+        continue
+      }
+      if (field == "memsize") {
+        // Send RAM size in megabytes. Rounding because sysinfo doesn't
+        // always provide RAM in multiples of 1024.
+        value = Math.round(value / 1024 / 1024)
+      }
+      ret[field] = value
+    }
+
+    let theme = LightweightThemeManager.currentTheme;
+    if (theme)
+      ret.persona = theme.id;
+
+    if (this._addons)
+      ret.addons = this._addons;
+
+    return ret;
+  },
+
+  /**
    * Pull values from about:memory into corresponding histograms
    */
   gatherMemory: function gatherMemory() {
     let mgr;
     try {
       mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
     } catch (e) {
@@ -291,20 +340,21 @@ TelemetryPing.prototype = {
   /**
    * Send data to the server. Record success/send-time in histograms
    */
   send: function send(reason, server) {
     // populate histograms one last time
     this.gatherMemory();
     let payload = {
       ver: PAYLOAD_VERSION,
-      info: getMetadata(reason),
+      info: this.getMetadata(reason),
       simpleMeasurements: getSimpleMeasurements(),
       histograms: getHistograms()
     };
+
     let isTestPing = (reason == "test-ping");
     // Generate a unique id once per session so the server can cope with duplicate submissions.
     // Use a deterministic url for testing.
     if (!this._path)
       this._path = "/submit/telemetry/" + (isTestPing ? reason : generateUUID());
     
     let hping = Telemetry.getHistogramById("TELEMETRY_PING");
     let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS");
@@ -400,16 +450,19 @@ TelemetryPing.prototype = {
   /**
    * This observer drives telemetry.
    */
   observe: function (aSubject, aTopic, aData) {
     // Allows to change the server for testing
     var server = this._server;
 
     switch (aTopic) {
+    case "Add-ons":
+      this._addons = aData;
+      break;
     case "profile-after-change":
       this.setup();
       break;
     case "profile-before-change":
       this.uninstall();
       break;
     case "cycle-collector-begin":
       let now = new Date();
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
@@ -5,27 +5,29 @@
  * 
  * Telemetry code keeps histograms of past telemetry pings. The first
  * ping populates these histograms. One of those histograms is then
  * checked in the second request.
  */
 
 do_load_httpd_js();
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
 
 const PATH = "/submit/telemetry/test-ping";
 const SERVER = "http://localhost:4444";
 const IGNORE_HISTOGRAM = "test::ignore_me";
 
 const BinaryInputStream = Components.Constructor(
   "@mozilla.org/binaryinputstream;1",
   "nsIBinaryInputStream",
   "setInputStream");
 
 var httpserver = new nsHttpServer();
+var gFinished = false;
 
 function telemetry_ping () {
   const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
   TelemetryPing.observe(null, "test-ping", SERVER);
 }
 
 function nonexistentServerObserver(aSubject, aTopic, aData) {
   Services.obs.removeObserver(nonexistentServerObserver, aTopic);
@@ -42,37 +44,22 @@ function telemetryObserver(aSubject, aTo
   Services.obs.removeObserver(telemetryObserver, aTopic);
   httpserver.registerPathHandler(PATH, checkHistograms);
   const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
   Telemetry.newHistogram(IGNORE_HISTOGRAM, 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN);
   Services.startup.interrupted = true;
   telemetry_ping();
 }
 
-function run_test() {
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
-  Services.obs.addObserver(nonexistentServerObserver, "telemetry-test-xhr-complete", false);
-  telemetry_ping();
-  // spin the event loop
-  do_test_pending();
-}
-
-function readBytesFromInputStream(inputStream, count) {
-  if (!count) {
-    count = inputStream.available();
-  }
-  return new BinaryInputStream(inputStream).readBytes(count);
-}
-
 function checkHistograms(request, response) {
   // do not need the http server anymore
   httpserver.stop(do_test_finished);
-  let s = request.bodyInputStream
+  let s = request.bodyInputStream;
   let payload = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON)
-                                             .decode(readBytesFromInputStream(s))
+                                             .decodeFromStream(s, s.available());
 
   do_check_eq(request.getHeader("content-type"), "application/json; charset=UTF-8");
   do_check_true(payload.simpleMeasurements.uptime >= 0)
   do_check_true(payload.simpleMeasurements.startupInterrupted === 1);
   // get rid of the non-deterministic field
   const expected_info = {
     reason: "test-ping",
     OS: "XPCShell", 
@@ -98,16 +85,17 @@ function checkHistograms(request, respon
     bucket_count: 3,
     histogram_type: 2,
     values: {0:1, 1:1, 2:0},
     sum: 1
   }
   let tc = payload.histograms[TELEMETRY_SUCCESS]
   do_check_eq(uneval(tc), 
               uneval(expected_tc));
+  gFinished = true;
 }
 
 // copied from toolkit/mozapps/extensions/test/xpcshell/head_addons.js
 const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
 const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
 
 function createAppInfo(id, name, version, platformVersion) {
   gAppInfo = {
@@ -148,8 +136,39 @@ function createAppInfo(id, name, version
         throw Components.results.NS_ERROR_NO_AGGREGATION;
       return gAppInfo.QueryInterface(iid);
     }
   };
   var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
   registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
                             XULAPPINFO_CONTRACTID, XULAppInfoFactory);
 }
+
+function dummyTheme(id) {
+  return {
+    id: id,
+    name: Math.random().toString(),
+    headerURL: "http://lwttest.invalid/a.png",
+    footerURL: "http://lwttest.invalid/b.png",
+    textcolor: Math.random().toString(),
+    accentcolor: Math.random().toString()
+  };
+}
+
+function run_test() {
+  // Addon manager needs a profile directory
+  do_get_profile();
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+  // try to make LightweightThemeManager do stuff
+  let gInternalManager = Cc["@mozilla.org/addons/integration;1"]
+                         .getService(Ci.nsIObserver)
+                         .QueryInterface(Ci.nsITimerCallback);
+
+  gInternalManager.observe(null, "addons-startup", null);
+  LightweightThemeManager.currentTheme = dummyTheme("1234");
+
+  Services.obs.addObserver(nonexistentServerObserver, "telemetry-test-xhr-complete", false);
+  telemetry_ping();
+  // spin the event loop
+  do_test_pending();
+  // ensure that test runs to completion
+  do_register_cleanup(function () do_check_true(gFinished))
+ }
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -1757,16 +1757,19 @@ var XPIProvider = {
     let data = this.enabledAddons;
     for (let id in this.bootstrappedAddons)
       data += (data ? "," : "") + id + ":" + this.bootstrappedAddons[id].version;
 
     try {
       Services.appinfo.annotateCrashReport("Add-ons", data);
     }
     catch (e) { }
+    
+    const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
+    TelemetryPing.observe(null, "Add-ons", data);
   },
 
   /**
    * Gets the add-on states for an install location.
    * This function may be expensive because of the recursiveLastModifiedTime call.
    *
    * @param  location
    *         The install location to retrieve the add-on states for