Bug 1520321 - Adds Telemetry for BITS update downloads r=chutten,rstrong
authorKirk Steuber <ksteuber@mozilla.com>
Mon, 15 Apr 2019 19:44:59 +0000
changeset 469556 fb9ff7baed4347cf0df3f252344a9f69e6942b44
parent 469555 84cc6e41ba1757fab96a5aed49d494d771bded5b
child 469557 2acb3832e7e5dc7fd89aab6069cb251b04dd153b
push id35874
push userccoroiu@mozilla.com
push dateTue, 16 Apr 2019 04:04:58 +0000
treeherdermozilla-central@be3f40425b52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschutten, rstrong
bugs1520321
milestone68.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 1520321 - Adds Telemetry for BITS update downloads r=chutten,rstrong These Histograms were added: UPDATE_CAN_USE_BITS_EXTERNAL UPDATE_CAN_USE_BITS_NOTIFY Used for telemetry indicating whether or not BITS can be used by this system. If BITS cannot be used, the probe will contain data indicating why not. UPDATE_BITS_RESULT_COMPLETE UPDATE_BITS_RESULT_PARTIAL Used for telemetry indicating whether the BITS update download succeeded. If it failed, the probe will contain data indicating how it failed. This scalar was added: update.bitshresult Used to indicate the hresult returned, if any, when a BITS download fails. Differential Revision: https://phabricator.services.mozilla.com/D25163
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/Scalars.yaml
toolkit/mozapps/update/UpdateService.jsm
toolkit/mozapps/update/UpdateTelemetry.jsm
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -6021,16 +6021,72 @@
     "alert_emails": ["application-update-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "categorical",
     "bug_numbers": [893505],
     "releaseChannelCollection": "opt-out",
     "description": "Update: the update action was initiated from the PanelUI application update menu item.",
     "labels": ["restart", "available", "manual"]
   },
+  "UPDATE_CAN_USE_BITS_EXTERNAL": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["application-update-telemetry-alerts@mozilla.com", "rstrong@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "categorical",
+    "labels": [
+      "CanUseBits",
+      "NoBits_NotWindows",
+      "NoBits_FeatureOff",
+      "NoBits_Pref",
+      "NoBits_Proxy",
+      "NoBits_OtherUser"
+    ],
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1343669, 1540193, 1520321],
+    "description": "Update: Whether BITS could be used to download updates (externally initiated)"
+  },
+  "UPDATE_CAN_USE_BITS_NOTIFY": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["application-update-telemetry-alerts@mozilla.com", "rstrong@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "categorical",
+    "labels": [
+      "CanUseBits",
+      "NoBits_NotWindows",
+      "NoBits_FeatureOff",
+      "NoBits_Pref",
+      "NoBits_Proxy",
+      "NoBits_OtherUser"
+    ],
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1343669, 1540193, 1520321],
+    "description": "Update: Whether BITS could be used to download updates (timer initiated)"
+  },
+  "UPDATE_BITS_RESULT_COMPLETE": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["application-update-telemetry-alerts@mozilla.com", "rstrong@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 99,
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1343669, 1540193, 1520321],
+    "description": "Update: Result code from downloading a complete update via BITS",
+    "operating_systems": ["windows"]
+  },
+  "UPDATE_BITS_RESULT_PARTIAL": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["application-update-telemetry-alerts@mozilla.com", "rstrong@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 99,
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1343669, 1540193, 1520321],
+    "description": "Update: Result code from downloading a partial update via BITS",
+    "operating_systems": ["windows"]
+  },
   "THUNDERBIRD_GLODA_SIZE_MB": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "never",
     "kind": "linear",
     "high": 1000,
     "n_buckets": 40,
     "description": "Gloda: size of global-messages-db.sqlite (MB)"
   },
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -2646,16 +2646,40 @@ update:
     notification_emails:
       - application-update-telemetry-alerts@mozilla.com
       - seceng-telemetry@mozilla.com
       - dkeeler@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - main
 
+  bitshresult:
+    bug_numbers:
+      - 1343669
+      - 1540193
+      - 1520321
+    description: >
+      If a BITS download fails on the Windows side (that is to say, BITS
+      ecounters an error rather than Firefox failing to interact with BITS),
+      it will likely give an hresult error indicating what happened. This probe
+      reports those error codes to allow us to see if BITS is commonly failing
+      on some systems. This probe is keyed on the type of update download,
+      either "PARTIAL" or "COMPLETE".
+    expires: never
+    kind: uint
+    keyed: true
+    notification_emails:
+      - application-update-telemetry-alerts@mozilla.com
+      - rstrong@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - main
+    operating_systems:
+      - windows
+
 # The following section contains search counters.
 browser.search:
   with_ads:
     bug_numbers:
       - 1495548
       - 1505411
     description: >
       Records counts of SERP pages with adverts displayed. The key format is ‘<engine-name>’.
--- a/toolkit/mozapps/update/UpdateService.jsm
+++ b/toolkit/mozapps/update/UpdateService.jsm
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 const {AUSTLMY} = ChromeUtils.import("resource://gre/modules/UpdateTelemetry.jsm");
-const {Bits, BitsRequest} =
+const {Bits, BitsRequest, BitsUnknownError, BitsVerificationError} =
   ChromeUtils.import("resource://gre/modules/Bits.jsm");
 const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser", "XMLHttpRequest"]);
 
@@ -560,16 +560,20 @@ function getCanStageUpdates() {
  *
  * @return A string with one of these values:
  *           CanUseBits
  *           NoBits_NotWindows
  *           NoBits_FeatureOff
  *           NoBits_Pref
  *           NoBits_Proxy
  *           NoBits_OtherUser
+ *         These strings are directly compatible with the categories for
+ *         UPDATE_CAN_USE_BITS_EXTERNAL and UPDATE_CAN_USE_BITS_NOTIFY telemetry
+ *         probes. If this function is made to return other values, they should
+ *         also be added to the labels lists for those probes in Histograms.json
  */
 function getCanUseBits() {
   if (AppConstants.platform != "win") {
     LOG("getCanUseBits - Not using BITS because this is not Windows");
     return "NoBits_NotWindows";
   }
   if (!AppConstants.MOZ_BITS_DOWNLOAD) {
     LOG("getCanUseBits - Not using BITS because the feature is disabled");
@@ -2311,16 +2315,23 @@ UpdateService.prototype = {
     // UPDATE_UNABLE_TO_APPLY_NOTIFY
     AUSTLMY.pingGeneric("UPDATE_UNABLE_TO_APPLY_" + this._pingSuffix,
                         getCanApplyUpdates(), true);
     // Histogram IDs:
     // UPDATE_CANNOT_STAGE_EXTERNAL
     // UPDATE_CANNOT_STAGE_NOTIFY
     AUSTLMY.pingGeneric("UPDATE_CANNOT_STAGE_" + this._pingSuffix,
                         getCanStageUpdates(), true);
+    if (AppConstants.platform == "win") {
+      // Histogram IDs:
+      // UPDATE_CAN_USE_BITS_EXTERNAL
+      // UPDATE_CAN_USE_BITS_NOTIFY
+      AUSTLMY.pingGeneric("UPDATE_CAN_USE_BITS_" + this._pingSuffix,
+                          getCanUseBits());
+    }
     // Histogram IDs:
     // UPDATE_INVALID_LASTUPDATETIME_EXTERNAL
     // UPDATE_INVALID_LASTUPDATETIME_NOTIFY
     // UPDATE_LAST_NOTIFY_INTERVAL_DAYS_EXTERNAL
     // UPDATE_LAST_NOTIFY_INTERVAL_DAYS_NOTIFY
     AUSTLMY.pingLastUpdateTime(this._pingSuffix);
     // Histogram IDs:
     // UPDATE_NOT_PREF_UPDATE_AUTO_EXTERNAL
@@ -3971,16 +3982,19 @@ Downloader.prototype = {
           Cc["@mozilla.org/updates/update-manager;1"].
             getService(Ci.nsIUpdateManager).saveUpdates();
 
           LOG("Downloader:downloadUpdate - Failed to start to BITS job. " +
               "Error: " + error);
         }
 
         this._pendingRequest = null;
+
+        AUSTLMY.pingBitsError(this.isCompleteUpdate, error);
+
         // Try download again with nsIIncrementalDownload
         // The update status file has already had STATE_DOWNLOADING written to
         // it. If the downloadUpdate call below returns early, that status
         // should probably be rewritten. However, the only conditions that might
         // cause it to return early would have prevented this code from running.
         // So it should be fine.
         this.downloadUpdate(this._update);
       });
@@ -4186,24 +4200,26 @@ Downloader.prototype = {
     if (!this.usingBits) {
       LOG("Downloader:onStopRequest - downloader: nsIIncrementalDownload, " +
           "original URI spec: " + request.URI.spec + ", final URI spec: " +
           request.finalURI.spec + ", status: " + status);
     } else {
       LOG("Downloader:onStopRequest - downloader: BITS, status: " + status);
     }
 
+    let bitsCompletionError;
     if (this.usingBits) {
       if (Components.isSuccessCode(status)) {
         try {
           await request.complete();
         } catch (e) {
           LOG("Downloader:onStopRequest - Unable to complete BITS download: " +
               e);
           status = Cr.NS_ERROR_FAILURE;
+          bitsCompletionError = e;
         }
       } else {
         // BITS jobs that failed to complete should still have cancel called on
         // them to remove the job.
         try {
           await request.cancelAsync();
         } catch (e) {
           // This will fail if the job stopped because it was cancelled.
@@ -4348,16 +4364,34 @@ Downloader.prototype = {
       // However, if the download was cancelled, don't retry. If the transfer
       // was cancelled, we don't want it to restart on its own.
       if (!Components.isSuccessCode(status) &&
           status != Cr.NS_BINDING_ABORTED &&
           status != Cr.NS_ERROR_ABORT) {
         deleteActiveUpdate = false;
         shouldRetrySoon = true;
       }
+
+      // Send BITS Telemetry
+      if (Components.isSuccessCode(status)) {
+        AUSTLMY.pingBitsSuccess(this.isCompleteUpdate);
+      } else {
+        let error;
+        if (bitsCompletionError) {
+          error = bitsCompletionError;
+        } else if (status == Cr.NS_ERROR_CORRUPTED_CONTENT) {
+          error = new BitsVerificationError();
+        } else {
+          error = request.transferError;
+          if (!error) {
+            error = new BitsUnknownError();
+          }
+        }
+        AUSTLMY.pingBitsError(this.isCompleteUpdate, error);
+      }
     }
 
     LOG("Downloader:onStopRequest - setting state to: " + state);
     if (this._patch.state != state) {
       this._patch.state = state;
     }
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
--- a/toolkit/mozapps/update/UpdateTelemetry.jsm
+++ b/toolkit/mozapps/update/UpdateTelemetry.jsm
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = [
   "AUSTLMY",
 ];
 
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+const {BitsError, BitsUnknownError} =
+  ChromeUtils.import("resource://gre/modules/Bits.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
 
 var AUSTLMY = {
   // Telemetry for the application update background update check occurs when
   // the background update timer fires after the update interval which is
   // determined by the app.update.interval preference and its telemetry
   // histogram IDs have the suffix '_NOTIFY'.
   // Telemetry for the externally initiated background update check occurs when
@@ -281,16 +284,120 @@ var AUSTLMY = {
       let key = aSuffix.toLowerCase().replace("_", "-");
       Services.telemetry.keyedScalarSet(id, key, aCode);
     } catch (e) {
       Cu.reportError(e);
     }
   },
 
   /**
+   * Records a failed BITS update download using Telemetry.
+   * In addition to the BITS Result histogram, this also sends an
+   * update.bitshresult scalar value.
+   *
+   * @param aIsComplete
+   *        If true the histogram is for a patch type complete, if false the
+   *        histogram is for a patch type partial. This will determine the
+   *        histogram id out of the following histogram ids:
+   *        UPDATE_BITS_RESULT_COMPLETE
+   *        UPDATE_BITS_RESULT_PARTIAL
+   *        This value is also used to determine the key for the keyed scalar
+   *        update.bitshresult (key is either "COMPLETE" or "PARTIAL")
+   * @param aError
+   *        The BitsError that occurred. See Bits.jsm for details on BitsError.
+   */
+  pingBitsError: function UT_pingBitsError(aIsComplete, aError) {
+    if (AppConstants.platform != "win") {
+      Cu.reportError("Warning: Attempted to submit BITS telemetry on a " +
+                     "non-Windows platform");
+      return;
+    }
+    if (!(aError instanceof BitsError)) {
+      Cu.reportError("Error sending BITS Error ping: Error is not a BitsError");
+      aError = new BitsUnknownError();
+    }
+    // Coerce the error to integer
+    let type = +aError.type;
+    if (isNaN(type)) {
+      Cu.reportError("Error sending BITS Error ping: Either error is not a " +
+                     "BitsError, or error type is not an integer.");
+      type = Ci.nsIBits.ERROR_TYPE_UNKNOWN;
+    } else if (type == Ci.nsIBits.ERROR_TYPE_SUCCESS) {
+      Cu.reportError("Error sending BITS Error ping: The error type must not " +
+                     "be the success type.");
+      type = Ci.nsIBits.ERROR_TYPE_UNKNOWN;
+    }
+    this._pingBitsResult(aIsComplete, type);
+
+    if (aError.codeType == Ci.nsIBits.ERROR_CODE_TYPE_HRESULT) {
+      let scalarKey;
+      if (aIsComplete) {
+        scalarKey = this.PATCH_COMPLETE;
+      } else {
+        scalarKey = this.PATCH_PARTIAL;
+      }
+      try {
+        Services.telemetry.keyedScalarSet("update.bitshresult", scalarKey,
+                                          aError.code);
+      } catch (e) {
+        Cu.reportError(e);
+      }
+    }
+  },
+
+  /**
+   * Records a successful BITS update download using Telemetry.
+   *
+   * @param aIsComplete
+   *        If true the histogram is for a patch type complete, if false the
+   *        histogram is for a patch type partial. This will determine the
+   *        histogram id out of the following histogram ids:
+   *        UPDATE_BITS_RESULT_COMPLETE
+   *        UPDATE_BITS_RESULT_PARTIAL
+   */
+  pingBitsSuccess: function UT_pingBitsSuccess(aIsComplete) {
+    if (AppConstants.platform != "win") {
+      Cu.reportError("Warning: Attempted to submit BITS telemetry on a " +
+                     "non-Windows platform");
+      return;
+    }
+    this._pingBitsResult(aIsComplete, Ci.nsIBits.ERROR_TYPE_SUCCESS);
+  },
+
+  /**
+   * This is the helper function that does all the work for pingBitsError and
+   * pingBitsSuccess. It submits a telemetry ping indicating the result of the
+   * BITS update download.
+   *
+   * @param aIsComplete
+   *        If true the histogram is for a patch type complete, if false the
+   *        histogram is for a patch type partial. This will determine the
+   *        histogram id out of the following histogram ids:
+   *        UPDATE_BITS_RESULT_COMPLETE
+   *        UPDATE_BITS_RESULT_PARTIAL
+   * @param aResultType
+   *        The result code. This will be one of the ERROR_TYPE_* values defined
+   *        in the nsIBits interface.
+   */
+  _pingBitsResult: function UT_pingBitsResult(aIsComplete, aResultType) {
+    let patchType;
+    if (aIsComplete) {
+      patchType = this.PATCH_COMPLETE;
+    } else {
+      patchType = this.PATCH_PARTIAL;
+    }
+    try {
+      let id = "UPDATE_BITS_RESULT_" + patchType;
+      Services.telemetry.getHistogramById(id).add(aResultType);
+    } catch (e) {
+      Cu.reportError(e);
+    }
+  },
+
+  /**
    * Submit the interval in days since the last notification for this background
    * update check or a boolean if the last notification is in the future.
    *
    * @param  aSuffix
    *         The histogram id suffix for histogram IDs:
    *         UPDATE_INVALID_LASTUPDATETIME_EXTERNAL
    *         UPDATE_INVALID_LASTUPDATETIME_NOTIFY
    *         UPDATE_LAST_NOTIFY_INTERVAL_DAYS_EXTERNAL