Bug 1346223 - Remove SessionRecorder.jsm. r=gfritzsche
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Mon, 24 Apr 2017 16:32:14 +0200
changeset 355686 7a15ded20926001f13ef322524a37797f77b74a4
parent 355685 b2572094f9fffb71820ec50b08fde070445d0bce
child 355687 96bc18217f464979975a004256a1c8b9cf011b72
push id89727
push userkwierso@gmail.com
push dateFri, 28 Apr 2017 23:47:07 +0000
treeherdermozilla-inbound@7274bcb9165f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfritzsche
bugs1346223
milestone55.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 1346223 - Remove SessionRecorder.jsm. r=gfritzsche This patch also moves the activeTicks logic to TelemetrySession.jsm along with the related test coverage. MozReview-Commit-ID: 8vXffqo2V85
toolkit/components/telemetry/TelemetryController.jsm
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
toolkit/components/telemetry/tests/unit/test_TelemetrySession_activeTicks.js
toolkit/components/telemetry/tests/unit/xpcshell.ini
toolkit/modules/SessionRecorder.jsm
toolkit/modules/moz.build
toolkit/modules/tests/xpcshell/test_session_recorder.js
toolkit/modules/tests/xpcshell/xpcshell.ini
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -29,17 +29,16 @@ const LOGGER_PREFIX = "TelemetryControll
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_BRANCH_LOG = PREF_BRANCH + "log.";
 const PREF_SERVER = PREF_BRANCH + "server";
 const PREF_LOG_LEVEL = PREF_BRANCH_LOG + "level";
 const PREF_LOG_DUMP = PREF_BRANCH_LOG + "dump";
 const PREF_CACHED_CLIENTID = PREF_BRANCH + "cachedClientID";
 const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
-const PREF_SESSIONS_BRANCH = "datareporting.sessions.";
 const PREF_UNIFIED = PREF_BRANCH + "unified";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
 const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_UNIFIED, false);
 
 const PING_FORMAT_VERSION = 4;
 
@@ -64,18 +63,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
                                   "resource://gre/modules/TelemetryStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
                                   "resource://gre/modules/ThirdPartyCookieProbe.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "SessionRecorder",
-                                  "resource://gre/modules/SessionRecorder.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive",
                                   "resource://gre/modules/TelemetryArchive.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
                                   "resource://gre/modules/TelemetrySession.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
@@ -296,24 +293,16 @@ this.TelemetryController = Object.freeze
     options.addClientId = aOptions.addClientId || false;
     options.addEnvironment = aOptions.addEnvironment || false;
     options.overwrite = aOptions.overwrite || false;
 
     return Impl.savePing(aType, aPayload, aFilePath, options);
   },
 
   /**
-   * The session recorder instance managed by Telemetry.
-   * @return {Object} The active SessionRecorder instance or null if not available.
-   */
-  getSessionRecorder() {
-    return Impl._sessionRecorder;
-  },
-
-  /**
    * Allows waiting for TelemetryControllers delayed initialization to complete.
    * The returned promise is guaranteed to resolve before TelemetryController is shutting down.
    * @return {Promise} Resolved when delayed TelemetryController initialization completed.
    */
   promiseInitialized() {
     return Impl.promiseInitialized();
   },
 });
@@ -328,18 +317,16 @@ var Impl = {
   // Undefined if this is not the first run, or the previous build ID is unknown.
   _previousBuildID: undefined,
   _clientID: null,
   // A task performing delayed initialization
   _delayedInitTask: null,
   // The deferred promise resolved when the initialization task completes.
   _delayedInitTaskDeferred: null,
 
-  // The session recorder, shared with FHR and the Data Reporting Service.
-  _sessionRecorder: null,
   // This is a public barrier Telemetry clients can use to add blockers to the shutdown
   // of TelemetryController.
   // After this barrier, clients can not submit Telemetry pings anymore.
   _shutdownBarrier: new AsyncShutdown.Barrier("TelemetryController: Waiting for clients."),
   // This is a private barrier blocked by pending async ping activity (sending & saving).
   _connectionsBarrier: new AsyncShutdown.Barrier("TelemetryController: Waiting for pending ping activity"),
   // This is true when running in the test infrastructure.
   _testMode: false,
@@ -687,22 +674,16 @@ var Impl = {
     // This will trigger displaying the datachoices infobar.
     TelemetryReportingPolicy.setup();
 
     if (!this.enableTelemetryRecording()) {
       this._log.config("setupChromeProcess - Telemetry recording is disabled, skipping Chrome process setup.");
       return Promise.resolve();
     }
 
-    // Initialize the session recorder.
-    if (!this._sessionRecorder) {
-      this._sessionRecorder = new SessionRecorder(PREF_SESSIONS_BRANCH);
-      this._sessionRecorder.onStartup();
-    }
-
     this._attachObservers();
 
     // Perform a lightweight, early initialization for the component, just registering
     // a few observers and initializing the session.
     TelemetrySession.earlyInit(this._testMode);
 
     // Annotate crash reports so that we get pings for startup crashes
     TelemetrySend.earlyInit();
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -600,16 +600,18 @@ this.TelemetrySession = Object.freeze({
   testReset() {
     Impl._sessionId = null;
     Impl._subsessionId = null;
     Impl._previousSessionId = null;
     Impl._previousSubsessionId = null;
     Impl._subsessionCounter = 0;
     Impl._profileSubsessionCounter = 0;
     Impl._subsessionStartActiveTicks = 0;
+    Impl._sessionActiveTicks = 0;
+    Impl._isUserActive = true;
     Impl._subsessionStartTimeMonotonic = 0;
     Impl._lastEnvironmentChangeDate = Policy.monotonicNow();
     this.testUninstall();
   },
   /**
    * Triggers shutdown of the module.
    */
   shutdown() {
@@ -656,16 +658,20 @@ this.TelemetrySession = Object.freeze({
 var Impl = {
   _histograms: {},
   _initialized: false,
   _logger: null,
   _prevValues: {},
   _slowSQLStartup: {},
   _hasWindowRestoredObserver: false,
   _hasXulWindowVisibleObserver: false,
+  _hasActiveTicksObservers: false,
+  // The activity state for the user. If false, don't count the next
+  // active tick. Otherwise, increment the active ticks as usual.
+  _isUserActive: true,
   _startupIO: {},
   // The previous build ID, if this is the first run with a new build.
   // Null if this is the first run, or the previous build ID is unknown.
   _previousBuildId: null,
   // Telemetry payloads sent by child processes.
   // Each element is in the format {source: <weak-ref>, payload: <object>},
   // where source is a weak reference to the child process,
   // and payload is the telemetry payload from that child process.
@@ -697,16 +703,18 @@ var Impl = {
   _profileSubsessionCounter: 0,
   // Date of the last session split
   _subsessionStartDate: null,
   // Start time of the current subsession using a monotonic clock for the subsession
   // length measurements.
   _subsessionStartTimeMonotonic: 0,
   // The active ticks counted when the subsession starts
   _subsessionStartActiveTicks: 0,
+  // Active ticks in the whole session.
+  _sessionActiveTicks: 0,
   // A task performing delayed initialization of the chrome process
   _delayedInitTask: null,
   // Need a timeout in case children are tardy in giving back their memory reports.
   _totalMemoryTimeout: undefined,
   _testing: false,
   // An accumulator of total memory across all processes. Only valid once the final child reports.
   _totalMemory: null,
   // A Set of outstanding USS report ids
@@ -800,30 +808,26 @@ var Impl = {
     if (failedProfileLockCount)
       ret.failedProfileLockCount = failedProfileLockCount;
 
     for (let ioCounter in this._startupIO)
       ret[ioCounter] = this._startupIO[ioCounter];
 
     ret.savedPings = TelemetryStorage.pendingPingCount;
 
-    ret.activeTicks = -1;
-    let sr = TelemetryController.getSessionRecorder();
-    if (sr) {
-      let activeTicks = sr.activeTicks;
-      if (isSubsession) {
-        activeTicks = sr.activeTicks - this._subsessionStartActiveTicks;
-      }
+    let activeTicks = this._sessionActiveTicks;
+    if (isSubsession) {
+      activeTicks = this._sessionActiveTicks - this._subsessionStartActiveTicks;
+    }
 
-      if (clearSubsession) {
-        this._subsessionStartActiveTicks = activeTicks;
-      }
+    if (clearSubsession) {
+      this._subsessionStartActiveTicks = activeTicks;
+    }
 
-      ret.activeTicks = activeTicks;
-    }
+    ret.activeTicks = activeTicks;
 
     ret.pingsOverdue = TelemetrySend.overduePingsCount;
 
     return ret;
   },
 
   /**
    * When reflecting a histogram into JS, Telemetry hands us an object
@@ -1412,16 +1416,35 @@ var Impl = {
     let payload = this.getSessionPayload(reason, isSubsession);
     let options = {
       addClientId: true,
       addEnvironment: true,
     };
     return TelemetryController.submitExternalPing(getPingType(payload), payload, options);
   },
 
+  /**
+   * Attaches the needed observers during Telemetry early init, in the
+   * chrome process.
+   */
+  attachEarlyObservers() {
+    Services.obs.addObserver(this, "sessionstore-windows-restored");
+    if (AppConstants.platform === "android") {
+      Services.obs.addObserver(this, "application-background");
+    }
+    Services.obs.addObserver(this, "xul-window-visible");
+    this._hasWindowRestoredObserver = true;
+    this._hasXulWindowVisibleObserver = true;
+
+    // Attach the active-ticks related observers.
+    Services.obs.addObserver(this, "user-interaction-active");
+    Services.obs.addObserver(this, "user-interaction-inactive");
+    this._hasActiveTicksObservers = true;
+  },
+
   attachObservers: function attachObservers() {
     if (!this._initialized)
       return;
     Services.obs.addObserver(this, "idle-daily");
     if (Telemetry.canRecordExtended) {
       Services.obs.addObserver(this, TOPIC_CYCLE_COLLECTOR_BEGIN);
     }
   },
@@ -1477,23 +1500,17 @@ var Impl = {
     let previousBuildId = Preferences.get(PREF_PREVIOUS_BUILDID, null);
     let thisBuildID = Services.appinfo.appBuildID;
     // If there is no previousBuildId preference, we send null to the server.
     if (previousBuildId != thisBuildID) {
       this._previousBuildId = previousBuildId;
       Preferences.set(PREF_PREVIOUS_BUILDID, thisBuildID);
     }
 
-    Services.obs.addObserver(this, "sessionstore-windows-restored");
-    if (AppConstants.platform === "android") {
-      Services.obs.addObserver(this, "application-background");
-    }
-    Services.obs.addObserver(this, "xul-window-visible");
-    this._hasWindowRestoredObserver = true;
-    this._hasXulWindowVisibleObserver = true;
+    this.attachEarlyObservers();
 
     ppml.addMessageListener(MESSAGE_TELEMETRY_PAYLOAD, this);
     ppml.addMessageListener(MESSAGE_TELEMETRY_THREAD_HANGS, this);
     ppml.addMessageListener(MESSAGE_TELEMETRY_USS, this);
   },
 
   /**
    * Does the "heavy" Telemetry initialization later on, so we
@@ -1799,17 +1816,16 @@ var Impl = {
       p.push(TelemetryController.submitExternalPing(getPingType(payload), payload, options)
                                 .catch(e => this._log.error("saveShutdownPings - failed to submit saved-session ping", e)));
     }
 
     // Wait on pings to be saved.
     return Promise.all(p);
   },
 
-
   testSavePendingPing() {
     let payload = this.getSessionPayload(REASON_SAVED_SESSION, false);
     let options = {
       addClientId: true,
       addEnvironment: true,
       overwrite: true,
     };
     return TelemetryController.addPendingPing(getPingType(payload), payload, options);
@@ -1826,16 +1842,21 @@ var Impl = {
     }
     if (this._hasXulWindowVisibleObserver) {
       Services.obs.removeObserver(this, "xul-window-visible");
       this._hasXulWindowVisibleObserver = false;
     }
     if (AppConstants.platform === "android") {
       Services.obs.removeObserver(this, "application-background");
     }
+    if (this._hasActiveTicksObservers) {
+      Services.obs.removeObserver(this, "user-interaction-active");
+      Services.obs.removeObserver(this, "user-interaction-inactive");
+      this._hasActiveTicksObservers = false;
+    }
     GCTelemetry.shutdown();
   },
 
   getPayload: function getPayload(reason, clearSubsession) {
     this._log.trace("getPayload - clearSubsession: " + clearSubsession);
     reason = reason || REASON_GATHER_PAYLOAD;
     // This function returns the current Telemetry payload to the caller.
     // We only gather startup info once.
@@ -1904,16 +1925,30 @@ var Impl = {
     this._addons = aAddOns;
   },
 
   testPing: function testPing() {
     return this.send(REASON_TEST_PING);
   },
 
   /**
+   * Tracks the number of "ticks" the user was active in.
+   */
+  _onActiveTick(aUserActive) {
+    const needsUpdate = aUserActive && this._isUserActive;
+    this._isUserActive = aUserActive;
+
+    // Don't count the first active tick after we get out of
+    // inactivity, because it is just the start of this active tick.
+    if (needsUpdate) {
+      this._sessionActiveTicks++;
+    }
+  },
+
+  /**
    * This observer drives telemetry.
    */
   observe(aSubject, aTopic, aData) {
     // Prevent the cycle collector begin topic from cluttering the log.
     if (aTopic != TOPIC_CYCLE_COLLECTOR_BEGIN) {
       this._log.trace("observe - " + aTopic + " notified.");
     }
 
@@ -1983,16 +2018,22 @@ var Impl = {
       let payload = this.getSessionPayload(REASON_SAVED_SESSION, false);
       let options = {
         addClientId: true,
         addEnvironment: true,
         overwrite: true,
       };
       TelemetryController.addPendingPing(getPingType(payload), payload, options);
       break;
+    case "user-interaction-active":
+      this._onActiveTick(true);
+      break;
+    case "user-interaction-inactive":
+      this._onActiveTick(false);
+      break;
     }
     return undefined;
   },
 
   /**
    * This tells TelemetrySession to uninitialize and save any pending pings.
    */
   shutdownChromeProcess() {
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -927,39 +927,39 @@ add_task(function* test_checkSubsessionH
 
 add_task(function* test_checkSubsessionData() {
   if (gIsAndroid) {
     // We don't support subsessions yet on Android.
     return;
   }
 
   // Keep track of the active ticks count if the session recorder is available.
-  let sessionRecorder = TelemetryController.getSessionRecorder();
-  let activeTicksAtSubsessionStart = sessionRecorder.activeTicks;
+  let getActiveTicks = () => TelemetrySession.getPayload().simpleMeasurements.activeTicks;
+  let activeTicksAtSubsessionStart = getActiveTicks();
   let expectedActiveTicks = activeTicksAtSubsessionStart;
 
   let incrementActiveTicks = () => {
-    sessionRecorder.incrementActiveTicks();
+    TelemetrySession.observe(null, "user-interaction-active");
     ++expectedActiveTicks;
   }
 
   yield TelemetryController.testReset();
 
   // Both classic and subsession payload data should be the same on the first subsession.
   incrementActiveTicks();
   let classic = TelemetrySession.getPayload();
   let subsession = TelemetrySession.getPayload("environment-change");
   Assert.equal(classic.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Classic pings must count active ticks since the beginning of the session.");
   Assert.equal(subsession.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Subsessions must count active ticks as classic pings on the first subsession.");
 
   // Start a new subsession and check that the active ticks are correctly reported.
   incrementActiveTicks();
-  activeTicksAtSubsessionStart = sessionRecorder.activeTicks;
+  activeTicksAtSubsessionStart = getActiveTicks();
   classic = TelemetrySession.getPayload();
   subsession = TelemetrySession.getPayload("environment-change", true);
   Assert.equal(classic.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Classic pings must count active ticks since the beginning of the session.");
   Assert.equal(subsession.simpleMeasurements.activeTicks, expectedActiveTicks,
                "Pings must not loose the tick count when starting a new subsession.");
 
   // Get a new subsession payload without clearing the subsession.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession_activeTicks.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+Cu.import("resource://gre/modules/TelemetryController.jsm", this);
+Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
+
+
+add_task(function* test_setup() {
+  // Addon manager needs a profile directory
+  do_get_profile();
+  loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+  // Make sure we don't generate unexpected pings due to pref changes.
+  yield setEmptyPrefWatchlist();
+
+  Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
+});
+
+add_task(function* test_record_activeTicks() {
+  yield TelemetryController.testSetup();
+
+  let checkActiveTicks = (expected) => {
+    let payload = TelemetrySession.getPayload();
+    Assert.equal(payload.simpleMeasurements.activeTicks, expected,
+                 "TelemetrySession must record the expected number of active ticks.");
+  };
+
+  for (let i = 0; i < 3; i++) {
+    Services.obs.notifyObservers(null, "user-interaction-active");
+  }
+  checkActiveTicks(3);
+
+  // Now send inactive. This must not increment the active ticks.
+  Services.obs.notifyObservers(null, "user-interaction-inactive");
+  checkActiveTicks(3);
+
+  // If we send active again, this should be counted as inactive.
+  Services.obs.notifyObservers(null, "user-interaction-active");
+  checkActiveTicks(3);
+
+  // If we send active again, this should be counted as active.
+  Services.obs.notifyObservers(null, "user-interaction-active");
+  checkActiveTicks(4);
+
+  Services.obs.notifyObservers(null, "user-interaction-active");
+  checkActiveTicks(5);
+
+  yield TelemetryController.testShutdown();
+});
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -48,16 +48,17 @@ tags = addons
 tags = addons
 [test_TelemetryStopwatch.js]
 [test_TelemetryControllerBuildID.js]
 [test_TelemetrySendOldPings.js]
 skip-if = os == "android" # Disabled due to intermittent orange on Android
 tags = addons
 [test_TelemetrySession.js]
 tags = addons
+[test_TelemetrySession_activeTicks.js]
 [test_ThreadHangStats.js]
 run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
 [test_TelemetrySend.js]
 [test_ChildHistograms.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 tags = addons
 [test_ChildScalars.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
deleted file mode 100644
--- a/toolkit/modules/SessionRecorder.jsm
+++ /dev/null
@@ -1,403 +0,0 @@
-/* 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";
-
-this.EXPORTED_SYMBOLS = [
-  "SessionRecorder",
-];
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-common/utils.js");
-
-// We automatically prune sessions older than this.
-const MAX_SESSION_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days.
-const STARTUP_RETRY_INTERVAL_MS = 5000;
-
-// Wait up to 5 minutes for startup measurements before giving up.
-const MAX_STARTUP_TRIES = 300000 / STARTUP_RETRY_INTERVAL_MS;
-
-const LOGGER_NAME = "Toolkit.Telemetry";
-const LOGGER_PREFIX = "SessionRecorder::";
-
-/**
- * Records information about browser sessions.
- *
- * This serves as an interface to both current session information as
- * well as a history of previous sessions.
- *
- * Typically only one instance of this will be installed in an
- * application. It is typically managed by an XPCOM service. The
- * instance is instantiated at application start; onStartup is called
- * once the profile is installed; onShutdown is called during shutdown.
- *
- * We currently record state in preferences. However, this should be
- * invisible to external consumers. We could easily swap in a different
- * storage mechanism if desired.
- *
- * Please note the different semantics for storing times and dates in
- * preferences. Full dates (notably the session start time) are stored
- * as strings because preferences have a 32-bit limit on integer values
- * and milliseconds since UNIX epoch would overflow. Many times are
- * stored as integer offsets from the session start time because they
- * should not overflow 32 bits.
- *
- * Since this records history of all sessions, there is a possibility
- * for unbounded data aggregation. This is curtailed through:
- *
- *   1) An "idle-daily" observer which delete sessions older than
- *      MAX_SESSION_AGE_MS.
- *   2) The creator of this instance explicitly calling
- *      `pruneOldSessions`.
- *
- * @param branch
- *        (string) Preferences branch on which to record state.
- */
-this.SessionRecorder = function(branch) {
-  if (!branch) {
-    throw new Error("branch argument must be defined.");
-  }
-
-  if (!branch.endsWith(".")) {
-    throw new Error("branch argument must end with '.': " + branch);
-  }
-
-  this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
-
-  this._prefs = new Preferences(branch);
-  this._lastActivityWasInactive = false;
-  this._activeTicks = 0;
-  this.fineTotalTime = 0;
-  this._started = false;
-  this._timer = null;
-  this._startupFieldTries = 0;
-
-  this._os = Cc["@mozilla.org/observer-service;1"]
-               .getService(Ci.nsIObserverService);
-
-};
-
-SessionRecorder.prototype = Object.freeze({
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
-
-  STARTUP_RETRY_INTERVAL_MS,
-
-  get _currentIndex() {
-    return this._prefs.get("currentIndex", 0);
-  },
-
-  set _currentIndex(value) {
-    this._prefs.set("currentIndex", value);
-  },
-
-  get _prunedIndex() {
-    return this._prefs.get("prunedIndex", 0);
-  },
-
-  set _prunedIndex(value) {
-    this._prefs.set("prunedIndex", value);
-  },
-
-  get startDate() {
-    return CommonUtils.getDatePref(this._prefs, "current.startTime");
-  },
-
-  set _startDate(value) {
-    CommonUtils.setDatePref(this._prefs, "current.startTime", value);
-  },
-
-  get activeTicks() {
-    return this._prefs.get("current.activeTicks", 0);
-  },
-
-  incrementActiveTicks() {
-    this._prefs.set("current.activeTicks", ++this._activeTicks);
-  },
-
-  /**
-   * Total time of this session in integer seconds.
-   *
-   * See also fineTotalTime for the time in milliseconds.
-   */
-  get totalTime() {
-    return this._prefs.get("current.totalTime", 0);
-  },
-
-  updateTotalTime() {
-    // We store millisecond precision internally to prevent drift from
-    // repeated rounding.
-    this.fineTotalTime = Date.now() - this.startDate;
-    this._prefs.set("current.totalTime", Math.floor(this.fineTotalTime / 1000));
-  },
-
-  get main() {
-    return this._prefs.get("current.main", -1);
-  },
-
-  set _main(value) {
-    if (!Number.isInteger(value)) {
-      throw new Error("main time must be an integer.");
-    }
-
-    this._prefs.set("current.main", value);
-  },
-
-  get firstPaint() {
-    return this._prefs.get("current.firstPaint", -1);
-  },
-
-  set _firstPaint(value) {
-    if (!Number.isInteger(value)) {
-      throw new Error("firstPaint must be an integer.");
-    }
-
-    this._prefs.set("current.firstPaint", value);
-  },
-
-  get sessionRestored() {
-    return this._prefs.get("current.sessionRestored", -1);
-  },
-
-  set _sessionRestored(value) {
-    if (!Number.isInteger(value)) {
-      throw new Error("sessionRestored must be an integer.");
-    }
-
-    this._prefs.set("current.sessionRestored", value);
-  },
-
-  getPreviousSessions() {
-    let result = {};
-
-    for (let i = this._prunedIndex; i < this._currentIndex; i++) {
-      let s = this.getPreviousSession(i);
-      if (!s) {
-        continue;
-      }
-
-      result[i] = s;
-    }
-
-    return result;
-  },
-
-  getPreviousSession(index) {
-    return this._deserialize(this._prefs.get("previous." + index));
-  },
-
-  /**
-   * Prunes old, completed sessions that started earlier than the
-   * specified date.
-   */
-  pruneOldSessions(date) {
-    for (let i = this._prunedIndex; i < this._currentIndex; i++) {
-      let s = this.getPreviousSession(i);
-      if (!s) {
-        continue;
-      }
-
-      if (s.startDate >= date) {
-        continue;
-      }
-
-      this._log.debug("Pruning session #" + i + ".");
-      this._prefs.reset("previous." + i);
-      this._prunedIndex = i;
-    }
-  },
-
-  recordStartupFields() {
-    let si = this._getStartupInfo();
-
-    if (!si.process) {
-      throw new Error("Startup info not available.");
-    }
-
-    let missing = false;
-
-    for (let field of ["main", "firstPaint", "sessionRestored"]) {
-      if (!(field in si)) {
-        this._log.debug("Missing startup field: " + field);
-        missing = true;
-        continue;
-      }
-
-      this["_" + field] = si[field].getTime() - si.process.getTime();
-    }
-
-    if (!missing || this._startupFieldTries > MAX_STARTUP_TRIES) {
-      this._clearStartupTimer();
-      return;
-    }
-
-    // If we have missing fields, install a timer and keep waiting for
-    // data.
-    this._startupFieldTries++;
-
-    if (!this._timer) {
-      this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      this._timer.initWithCallback({
-        notify: this.recordStartupFields.bind(this),
-      }, this.STARTUP_RETRY_INTERVAL_MS, this._timer.TYPE_REPEATING_SLACK);
-    }
-  },
-
-  _clearStartupTimer() {
-    if (this._timer) {
-      this._timer.cancel();
-      delete this._timer;
-    }
-  },
-
-  /**
-   * Perform functionality on application startup.
-   *
-   * This is typically called in a "profile-do-change" handler.
-   */
-  onStartup() {
-    if (this._started) {
-      throw new Error("onStartup has already been called.");
-    }
-
-    let si = this._getStartupInfo();
-    if (!si.process) {
-      throw new Error("Process information not available. Misconfigured app?");
-    }
-
-    this._started = true;
-
-    this._os.addObserver(this, "profile-before-change");
-    this._os.addObserver(this, "user-interaction-active");
-    this._os.addObserver(this, "user-interaction-inactive");
-    this._os.addObserver(this, "idle-daily");
-
-    // This has the side-effect of clearing current session state.
-    this._moveCurrentToPrevious();
-
-    this._startDate = si.process;
-    this._prefs.set("current.activeTicks", 0);
-    this.updateTotalTime();
-
-    this.recordStartupFields();
-  },
-
-  /**
-   * Record application activity.
-   */
-  onActivity(active) {
-    let updateActive = active && !this._lastActivityWasInactive;
-    this._lastActivityWasInactive = !active;
-
-    this.updateTotalTime();
-
-    if (updateActive) {
-      this.incrementActiveTicks();
-    }
-  },
-
-  onShutdown() {
-    this._log.info("Recording clean session shutdown.");
-    this._prefs.set("current.clean", true);
-    this.updateTotalTime();
-    this._clearStartupTimer();
-
-    this._os.removeObserver(this, "profile-before-change");
-    this._os.removeObserver(this, "user-interaction-active");
-    this._os.removeObserver(this, "user-interaction-inactive");
-    this._os.removeObserver(this, "idle-daily");
-  },
-
-  _CURRENT_PREFS: [
-    "current.startTime",
-    "current.activeTicks",
-    "current.totalTime",
-    "current.main",
-    "current.firstPaint",
-    "current.sessionRestored",
-    "current.clean",
-  ],
-
-  // This is meant to be called only during onStartup().
-  _moveCurrentToPrevious() {
-    try {
-      if (!this.startDate.getTime()) {
-        this._log.info("No previous session. Is this first app run?");
-        return;
-      }
-
-      let clean = this._prefs.get("current.clean", false);
-
-      let count = this._currentIndex++;
-      let obj = {
-        s: this.startDate.getTime(),
-        a: this.activeTicks,
-        t: this.totalTime,
-        c: clean,
-        m: this.main,
-        fp: this.firstPaint,
-        sr: this.sessionRestored,
-      };
-
-      this._log.debug("Recording last sessions as #" + count + ".");
-      this._prefs.set("previous." + count, JSON.stringify(obj));
-    } catch (ex) {
-      this._log.warn("Exception when migrating last session", ex);
-    } finally {
-      this._log.debug("Resetting prefs from last session.");
-      for (let pref of this._CURRENT_PREFS) {
-        this._prefs.reset(pref);
-      }
-    }
-  },
-
-  _deserialize(s) {
-    let o;
-    try {
-      o = JSON.parse(s);
-    } catch (ex) {
-      return null;
-    }
-
-    return {
-      startDate: new Date(o.s),
-      activeTicks: o.a,
-      totalTime: o.t,
-      clean: !!o.c,
-      main: o.m,
-      firstPaint: o.fp,
-      sessionRestored: o.sr,
-    };
-  },
-
-  // Implemented as a function to allow for monkeypatching in tests.
-  _getStartupInfo() {
-    return Cc["@mozilla.org/toolkit/app-startup;1"]
-             .getService(Ci.nsIAppStartup)
-             .getStartupInfo();
-  },
-
-  observe(subject, topic, data) {
-    switch (topic) {
-      case "profile-before-change":
-        this.onShutdown();
-        break;
-
-      case "user-interaction-active":
-        this.onActivity(true);
-        break;
-
-      case "user-interaction-inactive":
-        this.onActivity(false);
-        break;
-
-      case "idle-daily":
-        this.pruneOldSessions(new Date(Date.now() - MAX_SESSION_AGE_MS));
-        break;
-    }
-  },
-});
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -44,19 +44,16 @@ with Files('tests/xpcshell/test_NewTabUt
     BUG_COMPONENT = ('Firefox', 'New Tab Page')
 
 with Files('tests/xpcshell/test_UpdateUtils*.js'):
     BUG_COMPONENT = ('Toolkit', 'General')
 
 with Files('tests/xpcshell/test_client_id.js'):
     BUG_COMPONENT = ('Toolkit', 'Telemetry')
 
-with Files('tests/xpcshell/test_session_recorder.js'):
-    BUG_COMPONENT = ('Toolkit', 'Telemetry')
-
 with Files('AsyncPrefs.jsm'):
     BUG_COMPONENT = ('Core', 'Security: Process Sandboxing')
 
 with Files('CharsetMenu.jsm'):
     BUG_COMPONENT = ('Firefox', 'Toolbars and Customization')
 
 with Files('ClientID.jsm'):
     BUG_COMPONENT = ('Toolkit', 'Telemetry')
@@ -137,19 +134,16 @@ with Files('RemoteSecurityUI.jsm'):
     BUG_COMPONENT = ('Firefox', 'Tabbed Browser')
 
 with Files('RemoteWebProgress.jsm'):
     BUG_COMPONENT = ('Core', 'Document Navigation')
 
 with Files('ResponsivenessMonitor.jsm'):
     BUG_COMPONENT = ('Firefox', 'Migration')
 
-with Files('SessionRecorder.jsm'):
-    BUG_COMPONENT = ('Toolkit', 'Telemetry')
-
 with Files('ShortcutUtils.jsm'):
     BUG_COMPONENT = ('Firefox', 'Toolbars and Customization')
 
 with Files('Sqlite.jsm'):
     BUG_COMPONENT = ('Toolkit', 'Storage')
 
 with Files('UpdateUtils.jsm'):
     BUG_COMPONENT = ('Toolkit', 'Add-ons Manager')
@@ -236,17 +230,16 @@ EXTRA_JS_MODULES += [
     'ResetProfile.jsm',
     'ResponsivenessMonitor.jsm',
     'secondscreen/RokuApp.jsm',
     'secondscreen/SimpleServiceDiscovery.jsm',
     'SelectContentHelper.jsm',
     'SelectParentHelper.jsm',
     'ServiceRequest.jsm',
     'Services.jsm',
-    'SessionRecorder.jsm',
     'sessionstore/FormData.jsm',
     'sessionstore/ScrollPosition.jsm',
     'sessionstore/XPathGenerator.jsm',
     'ShortcutUtils.jsm',
     'Sntp.jsm',
     'Sqlite.jsm',
     'Task.jsm',
     'Timer.jsm',
deleted file mode 100644
--- a/toolkit/modules/tests/xpcshell/test_session_recorder.js
+++ /dev/null
@@ -1,305 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-var {utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/SessionRecorder.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://services-common/utils.js");
-
-
-function run_test() {
-  run_next_test();
-}
-
-function monkeypatchStartupInfo(recorder, start = new Date(), offset = 500) {
-  Object.defineProperty(recorder, "_getStartupInfo", {
-    value: function _getStartupInfo() {
-      return {
-        process: start,
-        main: new Date(start.getTime() + offset),
-        firstPaint: new Date(start.getTime() + 2 * offset),
-        sessionRestored: new Date(start.getTime() + 3 * offset),
-      };
-    }
-  });
-}
-
-function sleep(wait) {
-  let deferred = Promise.defer();
-
-  CommonUtils.namedTimer(function onTimer() {
-    deferred.resolve();
-  }, wait, deferred.promise, "_sleepTimer");
-
-  return deferred.promise;
-}
-
-function getRecorder(name, start, offset) {
-  let recorder = new SessionRecorder("testing." + name + ".");
-  monkeypatchStartupInfo(recorder, start, offset);
-
-  return recorder;
-}
-
-add_test(function test_basic() {
-  let recorder = getRecorder("basic");
-  recorder.onStartup();
-  recorder.onShutdown();
-
-  run_next_test();
-});
-
-add_task(function* test_current_properties() {
-  let now = new Date();
-  let recorder = getRecorder("current_properties", now);
-  yield sleep(25);
-  recorder.onStartup();
-
-  do_check_eq(recorder.startDate.getTime(), now.getTime());
-  do_check_eq(recorder.activeTicks, 0);
-  do_check_true(recorder.fineTotalTime > 0);
-  do_check_eq(recorder.main, 500);
-  do_check_eq(recorder.firstPaint, 1000);
-  do_check_eq(recorder.sessionRestored, 1500);
-
-  recorder.incrementActiveTicks();
-  do_check_eq(recorder.activeTicks, 1);
-
-  recorder._startDate = new Date(Date.now() - 1000);
-  recorder.updateTotalTime();
-  do_check_eq(recorder.totalTime, 1);
-
-  recorder.onShutdown();
-});
-
-// If startup info isn't present yet, we should install a timer and get
-// it eventually.
-add_task(function* test_current_availability() {
-  let recorder = new SessionRecorder("testing.current_availability.");
-  let now = new Date();
-
-  Object.defineProperty(recorder, "_getStartupInfo", {
-    value: function _getStartupInfo() {
-      return {
-        process: now,
-        main: new Date(now.getTime() + 500),
-        firstPaint: new Date(now.getTime() + 1000),
-      };
-    },
-    writable: true,
-  });
-
-  Object.defineProperty(recorder, "STARTUP_RETRY_INTERVAL_MS", {
-    value: 100,
-  });
-
-  let oldRecord = recorder.recordStartupFields;
-  let recordCount = 0;
-
-  Object.defineProperty(recorder, "recordStartupFields", {
-    value() {
-      recordCount++;
-      return oldRecord.call(recorder);
-    }
-  });
-
-  do_check_null(recorder._timer);
-  recorder.onStartup();
-  do_check_eq(recordCount, 1);
-  do_check_eq(recorder.sessionRestored, -1);
-  do_check_neq(recorder._timer, null);
-
-  yield sleep(125);
-  do_check_eq(recordCount, 2);
-  yield sleep(100);
-  do_check_eq(recordCount, 3);
-  do_check_eq(recorder.sessionRestored, -1);
-
-  monkeypatchStartupInfo(recorder, now);
-  yield sleep(100);
-  do_check_eq(recordCount, 4);
-  do_check_eq(recorder.sessionRestored, 1500);
-
-  // The timer should be removed and we should not fire again.
-  do_check_null(recorder._timer);
-  yield sleep(100);
-  do_check_eq(recordCount, 4);
-
-  recorder.onShutdown();
-});
-
-add_test(function test_timer_clear_on_shutdown() {
-  let recorder = new SessionRecorder("testing.timer_clear_on_shutdown.");
-  let now = new Date();
-
-  Object.defineProperty(recorder, "_getStartupInfo", {
-    value: function _getStartupInfo() {
-      return {
-        process: now,
-        main: new Date(now.getTime() + 500),
-        firstPaint: new Date(now.getTime() + 1000),
-      };
-    },
-  });
-
-  do_check_null(recorder._timer);
-  recorder.onStartup();
-  do_check_neq(recorder._timer, null);
-
-  recorder.onShutdown();
-  do_check_null(recorder._timer);
-
-  run_next_test();
-});
-
-add_task(function* test_previous_clean() {
-  let now = new Date();
-  let recorder = getRecorder("previous_clean", now);
-  yield sleep(25);
-  recorder.onStartup();
-
-  recorder.incrementActiveTicks();
-  recorder.incrementActiveTicks();
-
-  yield sleep(25);
-  recorder.onShutdown();
-
-  let total = recorder.totalTime;
-
-  yield sleep(25);
-  let now2 = new Date();
-  let recorder2 = getRecorder("previous_clean", now2, 100);
-  yield sleep(25);
-  recorder2.onStartup();
-
-  do_check_eq(recorder2.startDate.getTime(), now2.getTime());
-  do_check_eq(recorder2.main, 100);
-  do_check_eq(recorder2.firstPaint, 200);
-  do_check_eq(recorder2.sessionRestored, 300);
-
-  let sessions = recorder2.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 1);
-  do_check_true(0 in sessions);
-  let session = sessions[0];
-  do_check_true(session.clean);
-  do_check_eq(session.startDate.getTime(), now.getTime());
-  do_check_eq(session.main, 500);
-  do_check_eq(session.firstPaint, 1000);
-  do_check_eq(session.sessionRestored, 1500);
-  do_check_eq(session.totalTime, total);
-  do_check_eq(session.activeTicks, 2);
-
-  recorder2.onShutdown();
-});
-
-add_task(function* test_previous_abort() {
-  let now = new Date();
-  let recorder = getRecorder("previous_abort", now);
-  yield sleep(25);
-  recorder.onStartup();
-  recorder.incrementActiveTicks();
-  yield sleep(25);
-  let total = recorder.totalTime;
-  yield sleep(25);
-
-  let now2 = new Date();
-  let recorder2 = getRecorder("previous_abort", now2);
-  yield sleep(25);
-  recorder2.onStartup();
-
-  let sessions = recorder2.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 1);
-  do_check_true(0 in sessions);
-  let session = sessions[0];
-  do_check_false(session.clean);
-  do_check_eq(session.totalTime, total);
-
-  recorder.onShutdown();
-  recorder2.onShutdown();
-});
-
-add_task(function* test_multiple_sessions() {
-  for (let i = 0; i < 10; i++) {
-    let recorder = getRecorder("multiple_sessions");
-    yield sleep(25);
-    recorder.onStartup();
-    for (let j = 0; j < i; j++) {
-      recorder.incrementActiveTicks();
-    }
-    yield sleep(25);
-    recorder.onShutdown();
-    yield sleep(25);
-  }
-
-  let recorder = getRecorder("multiple_sessions");
-  recorder.onStartup();
-
-  let sessions = recorder.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 10);
-
-  for (let [i, session] of Object.entries(sessions)) {
-    do_check_eq(session.activeTicks, i);
-
-    if (i > 0) {
-      do_check_true(session.startDate.getTime() > sessions[i - 1].startDate.getTime());
-    }
-  }
-
-  // #6 is preserved since >=.
-  let threshold = sessions[6].startDate;
-  recorder.pruneOldSessions(threshold);
-
-  sessions = recorder.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 4);
-
-  recorder.pruneOldSessions(threshold);
-  sessions = recorder.getPreviousSessions();
-  do_check_eq(Object.keys(sessions).length, 4);
-  do_check_eq(recorder._prunedIndex, 5);
-
-  recorder.onShutdown();
-});
-
-add_task(function* test_record_activity() {
-  let recorder = getRecorder("record_activity");
-  yield sleep(25);
-  recorder.onStartup();
-  let total = recorder.totalTime;
-  yield sleep(25);
-
-  for (let i = 0; i < 3; i++) {
-    Services.obs.notifyObservers(null, "user-interaction-active");
-    yield sleep(25);
-    do_check_true(recorder.fineTotalTime > total);
-    total = recorder.fineTotalTime;
-  }
-
-  do_check_eq(recorder.activeTicks, 3);
-
-  // Now send inactive. We should increment total time but not active.
-  Services.obs.notifyObservers(null, "user-interaction-inactive");
-  do_check_eq(recorder.activeTicks, 3);
-  do_check_true(recorder.fineTotalTime > total);
-  total = recorder.fineTotalTime;
-  yield sleep(25);
-
-  // If we send active again, this should be counted as inactive.
-  Services.obs.notifyObservers(null, "user-interaction-active");
-  do_check_eq(recorder.activeTicks, 3);
-  do_check_true(recorder.fineTotalTime > total);
-  total = recorder.fineTotalTime;
-  yield sleep(25);
-
-  // If we send active again, this should be counted as active.
-  Services.obs.notifyObservers(null, "user-interaction-active");
-  do_check_eq(recorder.activeTicks, 4);
-
-  Services.obs.notifyObservers(null, "user-interaction-active");
-  do_check_eq(recorder.activeTicks, 5);
-
-  recorder.onShutdown();
-});
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini
+++ b/toolkit/modules/tests/xpcshell/xpcshell.ini
@@ -49,18 +49,16 @@ skip-if = toolkit == 'android'
 [test_PromiseUtils.js]
 skip-if = toolkit == 'android'
 [test_propertyListsUtils.js]
 skip-if = toolkit == 'android'
 [test_readCertPrefs.js]
 skip-if = toolkit == 'android'
 [test_Services.js]
 skip-if = toolkit == 'android'
-[test_session_recorder.js]
-skip-if = toolkit == 'android'
 [test_sqlite.js]
 skip-if = toolkit == 'android'
 [test_sqlite_shutdown.js]
 skip-if = toolkit == 'android'
 [test_task.js]
 skip-if = toolkit == 'android'
 [test_timer.js]
 skip-if = toolkit == 'android'