Bug 731025 - Add telemetry for migrator usage and errors, r=MattN,p=bsmedberg, a=lizzard
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Wed, 07 Oct 2015 13:34:46 +0100
changeset 296563 98fde280ecb42310790125e37f4d80d363372c3d
parent 296562 4124a1b6799936fc99c7d0a6d1ad26b74461a2d7
child 296564 d7170bf8704fc5ccac487b52963f3a801f5663f8
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)
reviewersMattN, lizzard
bugs731025
milestone43.0a2
Bug 731025 - Add telemetry for migrator usage and errors, r=MattN,p=bsmedberg, a=lizzard
browser/components/migration/MigrationUtils.jsm
browser/components/migration/content/migration.js
browser/components/migration/content/migration.xul
browser/components/places/content/places.js
toolkit/components/passwordmgr/content/passwordManager.js
toolkit/components/telemetry/Histograms.json
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -8,16 +8,17 @@ this.EXPORTED_SYMBOLS = ["MigrationUtils
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks";
 const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
                                   "resource://gre/modules/BookmarkHTMLUtils.jsm");
 
 var gMigrators = null;
 var gProfileStartup = null;
@@ -528,22 +529,31 @@ this.MigrationUtils = Object.freeze({
    * @see showMigrationWizard
    */
   get profileStartup() gProfileStartup,
 
   /**
    * Show the migration wizard.  On mac, this may just focus the wizard if it's
    * already running, in which case aOpener and aParams are ignored.
    *
-   * @param [optional] aOpener
-   *        the window that asks to open the wizard.
-   * @param [optioanl] aParams
-   *        arguments for the migration wizard, in the form of an nsIArray.
+   * @param {Window} [aOpener]
+   *        optional; the window that asks to open the wizard.
+   * @param {Array} [aParams]
+   *        optional arguments for the migration wizard, in the form of an array
    *        This is passed as-is for the params argument of
-   *        nsIWindowWatcher.openWindow.
+   *        nsIWindowWatcher.openWindow. The array elements we expect are, in
+   *        order:
+   *        - {Number} migration entry point constant (see below)
+   *        - {String} source browser identifier
+   *        - {nsIBrowserProfileMigrator} actual migrator object
+   *        - {Boolean} whether this is a startup migration
+   *        - {Boolean} whether to skip the 'source' page
+   *        NB: If you add new consumers, please add a migration entry point
+   *        constant below, and specify at least the first element of the array
+   *        (the migration entry point for purposes of telemetry).
    */
   showMigrationWizard:
   function MU_showMigrationWizard(aOpener, aParams) {
     let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no";
 #ifdef XP_MACOSX
     if (!this.isStartupMigration) {
       let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
       if (win) {
@@ -619,32 +629,53 @@ this.MigrationUtils = Object.freeze({
       catch(ex) {
         this.finishMigration();
         if (!(ex instanceof StopIteration))
           throw ex;
         return;
       }
     }
 
-    let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
-    let keyCSTR = Cc["@mozilla.org/supports-cstring;1"].
-                  createInstance(Ci.nsISupportsCString);
-    keyCSTR.data = migratorKey;
-    let skipImportSourcePageBool = Cc["@mozilla.org/supports-PRBool;1"].
-                                   createInstance(Ci.nsISupportsPRBool);
-    skipImportSourcePageBool.data = skipSourcePage;
-    params.appendElement(keyCSTR, false);
-    params.appendElement(migrator, false);
-    params.appendElement(aProfileStartup, false);
-    params.appendElement(skipImportSourcePageBool, false);
+    let migrationEntryPoint = this.MIGRATION_ENTRYPOINT_FIRSTRUN;
+    if (migrator && skipSourcePage && migratorKey == AppConstants.MOZ_APP_NAME) {
+      migrationEntryPoint = this.MIGRATION_ENTRYPOINT_FXREFRESH;
+    }
 
+    let params = [
+      migrationEntryPoint,
+      migratorKey,
+      migrator,
+      aProfileStartup,
+      skipSourcePage
+    ];
     this.showMigrationWizard(null, params);
   },
 
   /**
    * Cleans up references to migrators and nsIProfileInstance instances.
    */
   finishMigration: function MU_finishMigration() {
     gMigrators = null;
     gProfileStartup = null;
     gMigrationBundle = null;
-  }
+  },
+
+  MIGRATION_ENTRYPOINT_UNKNOWN: 0,
+  MIGRATION_ENTRYPOINT_FIRSTRUN: 1,
+  MIGRATION_ENTRYPOINT_FXREFRESH: 2,
+  MIGRATION_ENTRYPOINT_PLACES: 3,
+  MIGRATION_ENTRYPOINT_PASSWORDS: 4,
+
+  _sourceNameToIdMapping: {
+    "nothing":    1,
+    "firefox":    2,
+    "edge":       3,
+    "ie":         4,
+    "chrome":     5,
+    "chromium":   6,
+    "canary":     7,
+    "safari":     8,
+    "360se":      9,
+  },
+  getSourceIdForTelemetry(sourceName) {
+    return this._sourceNameToIdMapping[sourceName] || 0;
+  },
 });
--- a/browser/components/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -4,50 +4,52 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const kIMig = Ci.nsIBrowserProfileMigrator;
 const kIPStartup = Ci.nsIProfileStartup;
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 
 var MigrationWizard = {
   _source: "",                  // Source Profile Migrator ContractID suffix
   _itemsFlags: kIMig.ALL,       // Selected Import Data Sources (16-bit bitfield)
   _selectedProfile: null,       // Selected Profile name to import from
   _wiz: null,
   _migrator: null,
   _autoMigrate: null,
 
   init: function ()
   {
-    var os = Components.classes["@mozilla.org/observer-service;1"]
-                       .getService(Components.interfaces.nsIObserverService);
+    let os = Services.obs;
     os.addObserver(this, "Migration:Started", false);
     os.addObserver(this, "Migration:ItemBeforeMigrate", false);
     os.addObserver(this, "Migration:ItemAfterMigrate", false);
     os.addObserver(this, "Migration:ItemError", false);
     os.addObserver(this, "Migration:Ended", false);
 
     this._wiz = document.documentElement;
 
-    if ("arguments" in window && window.arguments.length > 1) {
-      this._source = window.arguments[0];
-      this._migrator = window.arguments[1] instanceof kIMig ?
-                       window.arguments[1] : null;
-      this._autoMigrate = window.arguments[2].QueryInterface(kIPStartup);
-      this._skipImportSourcePage = window.arguments[3];
+    let args = (window.arguments && window.arguments[0]) || [];
+    let entryPointId = args[0] || MigrationUtils.MIGRATION_ENTRYPOINT_UNKNOWN;
+    Services.telemetry.getHistogramById("FX_MIGRATION_ENTRY_POINT").add(entryPointId);
+
+    if (args.length > 1) {
+      this._source = args[1];
+      this._migrator = args[2] instanceof kIMig ?  args[2] : null;
+      this._autoMigrate = args[3].QueryInterface(kIPStartup);
+      this._skipImportSourcePage = args[4];
 
       if (this._autoMigrate) {
         // Show the "nothing" option in the automigrate case to provide an
         // easily identifiable way to avoid migration and create a new profile.
-        var nothing = document.getElementById("nothing");
-        nothing.hidden = false;
+        document.getElementById("nothing").hidden = false;
       }
     }
 
     this.onImportSourcePageShow();
   },
 
   uninit: function ()
   {
@@ -117,16 +119,21 @@ var MigrationWizard = {
     }
   },
   
   onImportSourcePageAdvanced: function ()
   {
     var newSource = document.getElementById("importSourceGroup").selectedItem.id;
     
     if (newSource == "nothing") {
+      // Need to do telemetry here because we're closing the dialog before we get to
+      // do actual migration. For actual migration, this doesn't happen until after
+      // migration takes place.
+      Services.telemetry.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
+                        .add(MigrationUtils.getSourceIdForTelemetry("nothing"));
       document.documentElement.cancel();
       return false;
     }
     
     if (!this._migrator || (newSource != this._source)) {
       // Create the migrator for the selected source.
       this._migrator = MigrationUtils.getMigrator(newSource);
 
@@ -352,22 +359,37 @@ var MigrationWizard = {
     this._wiz.canRewind = false;
     this._wiz.canAdvance = false;
     
     // When automigrating, show all of the data that can be received from this source.
     if (this._autoMigrate)
       this._itemsFlags = this._migrator.getMigrateData(this._selectedProfile, this._autoMigrate);
 
     this._listItems("migratingItems");
-    setTimeout(this.onMigratingMigrate, 0, this);
+    setTimeout(() => this.onMigratingMigrate(), 0);
   },
 
-  onMigratingMigrate: function (aOuter)
+  onMigratingMigrate: function ()
   {
-    aOuter._migrator.migrate(aOuter._itemsFlags, aOuter._autoMigrate, aOuter._selectedProfile);
+    this._migrator.migrate(this._itemsFlags, this._autoMigrate, this._selectedProfile);
+
+    Services.telemetry.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
+                      .add(MigrationUtils.getSourceIdForTelemetry(this._source));
+    if (!this._autoMigrate) {
+      let hist = Services.telemetry.getKeyedHistogramById("FX_MIGRATION_USAGE");
+      let exp = 0;
+      let items = this._itemsFlags;
+      while (items) {
+        if (items & 1) {
+          hist.add(this._source, exp);
+        }
+        items = items >> 1;
+        exp++
+      }
+    }
   },
   
   _listItems: function (aID)
   {
     var items = document.getElementById(aID);
     while (items.hasChildNodes())
       items.removeChild(items.firstChild);
 
@@ -404,16 +426,18 @@ var MigrationWizard = {
       break;
     case "Migration:ItemAfterMigrate":
       var label = document.getElementById(aData + "_migrated");
       if (label)
         label.removeAttribute("style");
       break;
     case "Migration:Ended":
       if (this._autoMigrate) {
+        Services.telemetry.getKeyedHistogramById("FX_MIGRATION_HOMEPAGE_IMPORTED")
+                          .add(this._source, !!this._newHomePage);
         if (this._newHomePage) {
           try {
             // set homepage properly
             var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
                                     .getService(Components.interfaces.nsIPrefService);
             var prefBranch = prefSvc.getBranch(null);
 
             if (this._newHomePage == "DEFAULT") {
@@ -446,18 +470,19 @@ var MigrationWizard = {
       }
       else {
         this._wiz.canAdvance = true;
         var nextButton = this._wiz.getButton("next");
         nextButton.click();
       }
       break;
     case "Migration:ItemError":
-      var type = "undefined";
-      switch (parseInt(aData)) {
+      let type = "undefined";
+      let numericType = parseInt(aData);
+      switch (numericType) {
       case Ci.nsIBrowserProfileMigrator.SETTINGS:
         type = "settings";
         break;
       case Ci.nsIBrowserProfileMigrator.COOKIES:
         type = "cookies";
         break;
       case Ci.nsIBrowserProfileMigrator.HISTORY:
         type = "history";
@@ -473,16 +498,18 @@ var MigrationWizard = {
         break;
       case Ci.nsIBrowserProfileMigrator.OTHERDATA:
         type = "misc. data";
         break;
       }
       Cc["@mozilla.org/consoleservice;1"]
         .getService(Ci.nsIConsoleService)
         .logStringMessage("some " + type + " did not successfully migrate.");
+      Services.telemetry.getKeyedHistogramById("FX_MIGRATION_ERRORS")
+                        .add(this._source, Math.log2(numericType));
       break;
     }
   },
 
   onDonePageShow: function ()
   {
     this._wiz.getButton("cancel").disabled = true;
     this._wiz.canRewind = false;
--- a/browser/components/migration/content/migration.xul
+++ b/browser/components/migration/content/migration.xul
@@ -27,16 +27,17 @@
 #ifdef XP_WIN
     <description id="importAll" control="importSourceGroup">&importFrom.label;</description>
 #else
     <description id="importAll" control="importSourceGroup">&importFromUnix.label;</description>
 #endif
     <description id="importBookmarks" control="importSourceGroup" hidden="true">&importFromBookmarks.label;</description>
 
     <radiogroup id="importSourceGroup" align="start">
+# NB: if you add items to this list, please also assign them a unique migrator ID in MigrationUtils.jsm
       <radio id="firefox"   label="&importFromFirefox.label;"   accesskey="&importFromFirefox.accesskey;"/>
 #ifdef XP_WIN
       <radio id="edge"      label="&importFromEdge.label;"      accesskey="&importFromEdge.accesskey;"/>
       <radio id="ie"        label="&importFromIE.label;"        accesskey="&importFromIE.accesskey;"/>
       <radio id="chrome"    label="&importFromChrome.label;"    accesskey="&importFromChrome.accesskey;"/>
       <radio id="chromium"  label="&importFromChromium.label;"  accesskey="&importFromChromium.accesskey;"/>
       <radio id="safari"    label="&importFromSafari.label;"    accesskey="&importFromSafari.accesskey;"/>
       <radio id="canary"    label="&importFromCanary.label;"    accesskey="&importFromCanary.accesskey;"/>
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -357,17 +357,18 @@ var PlacesOrganizer = {
     return PlacesUtils.asQuery(ContentArea.currentView.result.root).getQueries();
   },
 
   /**
    * Show the migration wizard for importing passwords,
    * cookies, history, preferences, and bookmarks.
    */
   importFromBrowser: function PO_importFromBrowser() {
-    MigrationUtils.showMigrationWizard(window);
+    // We pass in the type of source we're using for use in telemetry:
+    MigrationUtils.showMigrationWizard(window, [MigrationUtils.MIGRATION_ENTRYPOINT_PLACES]);
   },
 
   /**
    * Open a file-picker and import the selected file into the bookmarks store
    */
   importFromFile: function PO_importFromFile() {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     let fpCallback = function fpCallback_done(aResult) {
--- a/toolkit/components/passwordmgr/content/passwordManager.js
+++ b/toolkit/components/passwordmgr/content/passwordManager.js
@@ -480,10 +480,11 @@ function escapeKeyHandler() {
   if (signonsTree.getAttribute("editing")) {
     return;
   }
   window.close();
 }
 
 function OpenMigrator() {
   const { MigrationUtils } = Cu.import("resource:///modules/MigrationUtils.jsm", {});
-  MigrationUtils.showMigrationWizard(window);
+  // We pass in the type of source we're using for use in telemetry:
+  MigrationUtils.showMigrationWizard(window, [MigrationUtils.MIGRATION_ENTRYPOINT_PASSWORDS]);
 }
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4158,16 +4158,53 @@
     "extended_statistics_ok": true,
     "description": "THUMBNAILS: Time (ms) it takes to store a thumbnail in the cache"
   },
   "FX_THUMBNAILS_HIT_OR_MISS": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "THUMBNAILS: Thumbnail found"
   },
+  "FX_MIGRATION_ENTRY_POINT": {
+    "expires_in_version": "49",
+    "kind": "enumerated",
+    "n_values": 10,
+    "releaseChannelCollection": "opt-out",
+    "description": "Where the migration wizard was entered from. 0=Other/catch-all, 1=first-run, 2=refresh-firefox, 3=Places window, 4=Password manager"
+  },
+  "FX_MIGRATION_SOURCE_BROWSER": {
+    "expires_in_version": "49",
+    "kind": "enumerated",
+    "n_values": 15,
+    "releaseChannelCollection": "opt-out",
+    "description": "The browser that data is pulled from. The values correspond to the internal browser ID (see MigrationUtils.jsm)"
+  },
+  "FX_MIGRATION_ERRORS": {
+    "expires_in_version": "49",
+    "kind": "enumerated",
+    "keyed": "true",
+    "n_values": 12,
+    "releaseChannelCollection": "opt-out",
+    "description": "Errors encountered during migration in buckets defined by the datatype, keyed by the string description of the browser."
+  },
+  "FX_MIGRATION_USAGE": {
+    "expires_in_version": "49",
+    "kind": "enumerated",
+    "keyed": "true",
+    "n_values": 12,
+    "releaseChannelCollection": "opt-out",
+    "description": "Usage of migration for each datatype when migration is run through the post-firstrun flow which allows individual datatypes, keyed by the string description of the browser."
+  },
+  "FX_MIGRATION_HOMEPAGE_IMPORTED": {
+    "expires_in_version": "49",
+    "kind": "boolean",
+    "keyed": "true",
+    "releaseChannelCollection": "opt-out",
+    "description": "Whether the homepage was imported during browser migration. Only available on release builds during firstrun."
+  },
   "EVENTLOOP_UI_LAG_EXP_MS": {
     "alert_emails": ["perf-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 50,
     "high": "60000",
     "n_buckets": 20,
     "extended_statistics_ok": true,