Bug 1276196 - Add scalars to the main ping payload. r=gfritzsche
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Fri, 24 Jun 2016 08:58:00 +0200
changeset 302753 f5a2832bb5f9a9f5d31df70ca6557f3be6f9468e
parent 302752 33fbfeeb7e4eb54824d825aebb81e61916520a78
child 302754 8a4f5f3b73e2f4216ab75c7deaffc47f2a8fe032
push id78858
push userkwierso@gmail.com
push dateMon, 27 Jun 2016 21:09:22 +0000
treeherdermozilla-inbound@71f2ee2cd219 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfritzsche
bugs1276196
milestone50.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 1276196 - Add scalars to the main ping payload. r=gfritzsche
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/telemetry/docs/main-ping.rst
toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -959,16 +959,41 @@ var Impl = {
       for (let key of keys) {
         ret[id][key] = this.packHistogram(snapshot[key]);
       }
     }
 
     return ret;
   },
 
+  getScalars: function (subsession, clearSubsession) {
+    this._log.trace("getScalars - subsession: " + subsession + ", clearSubsession: " + clearSubsession);
+
+    if (!subsession) {
+      // We only support scalars for subsessions.
+      this._log.trace("getScalars - We only support scalars in subsessions.");
+      return {};
+    }
+
+    let scalarsSnapshot =
+      Telemetry.snapshotScalars(this.getDatasetType(), clearSubsession);
+
+    // Don't return the test scalars.
+    let ret = {};
+    for (let name in scalarsSnapshot) {
+      if (name.startsWith('telemetry.test') && this._testing == false) {
+        this._log.trace("getScalars - Skipping test scalar: " + name);
+      } else {
+        ret[name] = scalarsSnapshot[name];
+      }
+    }
+
+    return ret;
+  },
+
   getThreadHangStats: function getThreadHangStats(stats) {
     this._log.trace("getThreadHangStats");
 
     stats.forEach((thread) => {
       thread.activity = this.packHistogram(thread.activity);
       thread.hangs.forEach((hang) => {
         hang.histogram = this.packHistogram(hang.histogram);
       });
@@ -1228,16 +1253,17 @@ var Impl = {
     };
 
     // Payload common to chrome and content processes.
     let payloadObj = {
       ver: PAYLOAD_VERSION,
       simpleMeasurements: simpleMeasurements,
       histograms: protect(() => this.getHistograms(isSubsession, clearSubsession)),
       keyedHistograms: protect(() => this.getKeyedHistograms(isSubsession, clearSubsession)),
+      scalars: protect(() => this.getScalars(isSubsession, clearSubsession)),
     };
 
     // Add extended set measurements common to chrome & content processes
     if (Telemetry.canRecordExtended) {
       payloadObj.chromeHangs = protect(() => Telemetry.chromeHangs);
       payloadObj.threadHangStats = protect(() => this.getThreadHangStats(Telemetry.threadHangStats));
       payloadObj.log = protect(() => TelemetryLog.entries());
       payloadObj.webrtc = protect(() => Telemetry.webrtcStats);
--- a/toolkit/components/telemetry/docs/main-ping.rst
+++ b/toolkit/components/telemetry/docs/main-ping.rst
@@ -47,16 +47,17 @@ Structure::
       },
 
       childPayloads: [...], // only present with e10s; reduced payloads from content processes, null on failure
       simpleMeasurements: {...},
 
       // The following properties may all be null if we fail to collect them.
       histograms: {...},
       keyedHistograms: {...},
+      scalars: {...},
       chromeHangs: {...},
       threadHangStats: [...],
       log: [...],
       webrtc: {...},
       fileIOReports: {...},
       lateWrites: {...},
       addonDetails: {...},
       addonHistograms: {...},
@@ -177,16 +178,20 @@ histograms
 This section contains the histograms that are valid for the current platform. ``Flag`` and ``count`` histograms are always created and submitted, with their default value being respectively ``false`` and ``0``. Other histogram types (`see here <https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Adding_a_new_Telemetry_probe#Choosing_a_Histogram_Type>`_) are not created nor submitted if no data was added to them. The type and format of the reported histograms is described by the ``Histograms.json`` file. Its most recent version is available `here <https://dxr.mozilla.org/mozilla-central/source/toolkit/components/telemetry/Histograms.json>`_. The ``info.revision`` field indicates the revision of the file that describes the reported histograms.
 
 keyedHistograms
 ---------------
 This section contains the keyed histograms available for the current platform.
 
 As of Firefox 48, this section does not contain empty keyed histograms anymore.
 
+scalars
+----------
+This section contains the :doc:`scalars` that are valid for the current platform. Scalars are not created nor submitted if no data was added to them, and are only reported with subsession pings. Their type and format is described by the ``Scalars.yaml`` file. Its most recent version is available `here <https://dxr.mozilla.org/mozilla-central/source/toolkit/components/telemetry/Scalars.yaml>`_. The ``info.revision`` field indicates the revision of the file that describes the reported scalars.
+
 threadHangStats
 ---------------
 Contains the statistics about the hangs in main and background threads. Note that hangs in this section capture the [C++ pseudostack](https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Profiling_with_the_Built-in_Profiler#Native_stack_vs._Pseudo_stack) and an incomplete JS stack, which is not 100% precise.
 
 To avoid submitting overly large payloads, some limits are applied:
 
 * Identical, adjacent "(chrome script)" or "(content script)" stack entries are collapsed together. If a stack is reduced, the "(reduced stack)" frame marker is added as the oldest frame.
 * The depth of the reported stacks is limited to 11 entries. This value represents the 99.9th percentile of the thread hangs stack depths reported by Telemetry.
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -237,16 +237,40 @@ function checkPayloadInfo(data) {
             "Payload must contain an allowed reason.");
 
   Assert.ok(Date.parse(data.subsessionStartDate) >= Date.parse(data.sessionStartDate));
   Assert.ok(data.profileSubsessionCounter >= data.subsessionCounter);
   Assert.ok(data.timezoneOffset >= -12*60, "The timezone must be in a valid range.");
   Assert.ok(data.timezoneOffset <= 12*60, "The timezone must be in a valid range.");
 }
 
+function checkScalars(payload) {
+  // Check that the scalars section is available in the ping payload.
+  Assert.ok("scalars" in payload, "The scalars section must be available in the payload.");
+  Assert.equal(typeof payload.scalars, "object", "The scalars entry must be an object.");
+
+  // Check that we have valid scalar entries.
+  const scalars = payload.scalars;
+  for (let name in scalars) {
+    Assert.equal(typeof name, "string", "Scalar names must be strings.");
+    // Check if the value is of a supported type.
+    const valueType = typeof(scalars[name]);
+    if (valueType === "string") {
+      Assert.ok(scalars[name].length <= 50,
+                "String values can't have more than 50 characters");
+    } else if (valueType === "number") {
+      Assert.ok(scalars[name] >= 0,
+                "We only support unsigned integer values in scalars.");
+    } else {
+      Assert.ok(false,
+                name + " contains an unsupported value type (" + valueType + ")");
+    }
+  }
+}
+
 function checkPayload(payload, reason, successfulPings, savedPings) {
   Assert.ok("info" in payload, "Payload must contain an info section.");
   checkPayloadInfo(payload.info);
 
   Assert.ok(payload.simpleMeasurements.totalTime >= 0);
   Assert.ok(payload.simpleMeasurements.uptime >= 0);
   Assert.equal(payload.simpleMeasurements.startupInterrupted, 1);
   Assert.equal(payload.simpleMeasurements.shutdownDuration, SHUTDOWN_TIME);
@@ -373,16 +397,18 @@ function checkPayload(payload, reason, s
       range: [1, 2],
       bucket_count: 3,
       histogram_type: 4,
       values: {0:1, 1:0},
       sum: 1,
     },
   };
   Assert.deepEqual(expected_keyed_count, keyedHistograms[TELEMETRY_TEST_KEYED_COUNT]);
+
+  checkScalars(payload);
 }
 
 function writeStringToFile(file, contents) {
   let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
                 .createInstance(Ci.nsIFileOutputStream);
   ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
 	       RW_OWNER, ostream.DEFER_OPEN);
   ostream.write(contents, contents.length);
@@ -551,16 +577,76 @@ add_task(function* test_saveLoadPing() {
   }
 
   checkPingFormat(pings[0], PING_TYPE_MAIN, true, true);
   checkPayload(pings[0].payload, REASON_TEST_PING, 0, 1);
   checkPingFormat(pings[1], PING_TYPE_SAVED_SESSION, true, true);
   checkPayload(pings[1].payload, REASON_SAVED_SESSION, 0, 0);
 });
 
+add_task(function* test_checkSubsessionScalars() {
+  if (gIsAndroid) {
+    // We don't support subsessions yet on Android.
+    return;
+  }
+
+  // Clear the scalars.
+  Telemetry.clearScalars();
+  yield TelemetryController.testReset();
+
+  // Set some scalars.
+  const UINT_SCALAR = "telemetry.test.unsigned_int_kind";
+  const STRING_SCALAR = "telemetry.test.string_kind";
+  let expectedUint = 37;
+  let expectedString = "Test value. Yay.";
+  Telemetry.scalarSet(UINT_SCALAR, expectedUint);
+  Telemetry.scalarSet(STRING_SCALAR, expectedString);
+
+  // Check that scalars are not available in classic pings but are in subsession
+  // pings. Also clear the subsession.
+  let classic = TelemetrySession.getPayload();
+  let subsession = TelemetrySession.getPayload("environment-change", true);
+
+  const TEST_SCALARS = [ UINT_SCALAR, STRING_SCALAR ];
+  for (let name of TEST_SCALARS) {
+    // Scalar must be reported in subsession pings (e.g. main).
+    Assert.ok(name in subsession.scalars,
+              name + " must be reported in a subsession ping.");
+  }
+  // No scalar must be reported in classic pings (e.g. saved-session).
+  Assert.ok(Object.keys(classic.scalars).length == 0,
+            "Scalars must not be reported in a classic ping.");
+
+  // And make sure that we're getting the right values in the
+  // subsession ping.
+  Assert.equal(subsession.scalars[UINT_SCALAR], expectedUint,
+               UINT_SCALAR + " must contain the expected value.");
+  Assert.equal(subsession.scalars[STRING_SCALAR], expectedString,
+               STRING_SCALAR + " must contain the expected value.");
+
+  // Since we cleared the subsession in the last getPayload(), check that
+  // breaking subsessions clears the scalars.
+  subsession = TelemetrySession.getPayload("environment-change");
+  for (let name of TEST_SCALARS) {
+    Assert.ok(!(name in subsession.scalars),
+              name + " must be cleared with the new subsession.");
+  }
+
+  // Check if setting the scalars again works as expected.
+  expectedUint = 85;
+  expectedString = "A creative different value";
+  Telemetry.scalarSet(UINT_SCALAR, expectedUint);
+  Telemetry.scalarSet(STRING_SCALAR, expectedString);
+  subsession = TelemetrySession.getPayload("environment-change");
+  Assert.equal(subsession.scalars[UINT_SCALAR], expectedUint,
+               UINT_SCALAR + " must contain the expected value.");
+  Assert.equal(subsession.scalars[STRING_SCALAR], expectedString,
+               STRING_SCALAR + " must contain the expected value.");
+});
+
 add_task(function* test_checkSubsessionHistograms() {
   if (gIsAndroid) {
     // We don't support subsessions yet on Android.
     return;
   }
 
   let now = new Date(2020, 1, 1, 12, 0, 0);
   let expectedDate = new Date(2020, 1, 1, 0, 0, 0);