Bug 1458574 - Add histogram to track file age in profile directory. r?chutten draft
authorJan-Erik Rediger <jrediger@mozilla.com>
Mon, 28 May 2018 09:41:19 +0200
changeset 800466 697ae78f766aca05fd58c6d0516a049e9e48ff68
parent 800465 4a95f201136c5e86a2fa45876f4941dc1151e756
push id111367
push userbmo:jrediger@mozilla.com
push dateMon, 28 May 2018 07:49:00 +0000
reviewerschutten
bugs1458574
milestone62.0a1
Bug 1458574 - Add histogram to track file age in profile directory. r?chutten During every scan of the profile directory, we now record the age relative to the current date of each file seen. In theory we should rarely need to scan the profile directory, as on invocation it also stores the found time in a `times.json` file. We therefore don't need precise tracking above 4 years (~ 1500 days). One thing to note: ProfileAge might get re-entered, each invocation then starting a profile directory scan, as no `times.json` is found. The actual write of the file is done atomically, so the file should never be corrupted due to this. MozReview-Commit-ID: 7tfbUXDrnv5
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/Scalars.yaml
toolkit/modules/ProfileAge.jsm
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -13714,16 +13714,26 @@
     "record_in_processes": ["main"],
     "alert_emails": ["jhofmann@mozilla.com"],
     "bug_numbers": [1286118],
     "expires_in_version": "64",
     "kind": "boolean",
     "keyed": true,
     "description": "Permission requests (showing a permission prompt) by whether they were requested from code handling a user input event."
   },
+  "PROFILE_DIRECTORY_FILE_AGE": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["jrediger@mozilla.com", "telemetry-client-dev@mozilla.com"],
+    "bug_numbers": [1458574],
+    "expires_in_version": "65",
+    "high": 1500,
+    "n_buckets": 20,
+    "kind": "exponential",
+    "description": "Age of files scanned in profile directory in days when determining initial profile age."
+  },
   "HTMLEDITORS_WITH_RESIZERS": {
     "record_in_processes": ["content"],
     "alert_emails": ["mnakano@mozilla.com"],
     "bug_numbers": [1452538,1449564],
     "expires_in_version": "65",
     "kind": "boolean",
     "releaseChannelCollection": "opt-out",
     "description": "Number of HTML editors which has shown object resizers."
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -1219,16 +1219,30 @@ telemetry:
     keyed: true
     notification_emails:
       - telemetry-client-dev@mozilla.com
       - chutten@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - 'all'
 
+  profile_directory_scans:
+    bug_numbers:
+      - 1458574
+    description: >
+      The number of times a profile directory scan was initiated in ProfileAge.jsm
+    expires: "65"
+    kind: uint
+    notification_emails:
+      - jrediger@mozilla.com
+      - telemetry-client-dev@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'all'
+
 telemetry.discarded:
   accumulations:
     bug_numbers:
       - 1369041
     description: >
       Number of discarded accumulations to histograms in child processes
     expires: "never"
     kind: uint
--- a/toolkit/modules/ProfileAge.jsm
+++ b/toolkit/modules/ProfileAge.jsm
@@ -1,21 +1,33 @@
 /* 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";
 
 var EXPORTED_SYMBOLS = ["ProfileAge"];
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm");
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://services-common/utils.js");
 
 /**
+ * Calculate how many days passed between two dates.
+ * @param {Object} aStartDate The starting date.
+ * @param {Object} aEndDate The ending date.
+ * @return {Integer} The number of days between the two dates.
+ */
+function getElapsedTimeInDays(aStartDate, aEndDate) {
+  return TelemetryUtils.millisecondsToDays(aEndDate - aStartDate);
+}
+
+/**
  * Profile access to times.json (eg, creation/reset time).
  * This is separate from the provider to simplify testing and enable extraction
  * to a shared location in the future.
  */
 var ProfileAge = function(profile, log) {
   this.profilePath = profile || OS.Constants.Path.profileDir;
   if (!this.profilePath) {
     throw new Error("No profile directory.");
@@ -118,39 +130,48 @@ this.ProfileAge.prototype = {
   },
 
   /**
    * Traverse the contents of the profile directory, finding the oldest file
    * and returning its creation timestamp.
    */
   getOldestProfileTimestamp() {
     let self = this;
-    let oldest = Date.now() + 1000;
+    let start = Date.now();
+    let oldest = start + 1000;
     let iterator = new OS.File.DirectoryIterator(this.profilePath);
     self._log.debug("Iterating over profile " + this.profilePath);
     if (!iterator) {
       throw new Error("Unable to fetch oldest profile entry: no profile iterator.");
     }
 
+    Services.telemetry.scalarAdd("telemetry.profile_directory_scans", 1);
+    let histogram = Services.telemetry.getHistogramById("PROFILE_DIRECTORY_FILE_AGE");
+
     function onEntry(entry) {
       function onStatSuccess(info) {
         // OS.File doesn't seem to be behaving. See Bug 827148.
         // Let's do the best we can. This whole function is defensive.
         let date = info.winBirthDate || info.macBirthDate;
         if (!date || !date.getTime()) {
           // OS.File will only return file creation times of any kind on Mac
           // and Windows, where birthTime is defined.
           // That means we're unable to function on Linux, so we use mtime
           // instead.
           self._log.debug("No birth date. Using mtime.");
           date = info.lastModificationDate;
         }
 
         if (date) {
           let timestamp = date.getTime();
+          // Get the age relative to now.
+          // We don't care about dates in the future.
+          let age_in_days = Math.max(0, getElapsedTimeInDays(timestamp, start));
+          histogram.add(age_in_days);
+
           self._log.debug("Using date: " + entry.path + " = " + date);
           if (timestamp < oldest) {
             oldest = timestamp;
           }
         }
       }
 
       function onStatFailure(e) {