Bug 1278556 - Add scalar data from child processes to the main ping. r=chutten
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Mon, 16 Jan 2017 06:10:00 +0100
changeset 374590 94b3eae2ea2b1ca56097ef4306f911a5f67e0cfd
parent 374589 2ae922a0d331c978a184592fe3e281a24bc641f6
child 374591 f60de5dd2579154891c5c858a286eddd69371a59
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschutten
bugs1278556
milestone53.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 1278556 - Add scalar data from child processes to the main ping. r=chutten MozReview-Commit-ID: Ed4cV4XieRq
browser/modules/test/browser_UsageTelemetry.js
browser/modules/test/browser_UsageTelemetry_content.js
browser/modules/test/browser_UsageTelemetry_content_aboutHome.js
browser/modules/test/browser_UsageTelemetry_private_and_restore.js
browser/modules/test/browser_UsageTelemetry_searchbar.js
browser/modules/test/browser_UsageTelemetry_urlbar.js
browser/modules/test/head.js
toolkit/components/telemetry/Scalars.yaml
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/telemetry/tests/gtest/TestScalars.cpp
toolkit/components/telemetry/tests/unit/test_ChildScalars.js
toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js
toolkit/components/telemetry/tests/unit/xpcshell.ini
--- a/browser/modules/test/browser_UsageTelemetry.js
+++ b/browser/modules/test/browser_UsageTelemetry.js
@@ -56,18 +56,17 @@ let checkScalar = (scalars, scalarName, 
   }
   ok(!(scalarName in scalars), scalarName + " must not be reported.");
 };
 
 /**
  * Get a snapshot of the scalars and check them against the provided values.
  */
 let checkScalars = (countsObject) => {
-  const scalars =
-    Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   // Check the expected values. Scalars that are never set must not be reported.
   checkScalar(scalars, MAX_CONCURRENT_TABS, countsObject.maxTabs,
               "The maximum tab count must match the expected value.");
   checkScalar(scalars, TAB_EVENT_COUNT, countsObject.tabOpenCount,
               "The number of open tab event count must match the expected value.");
   checkScalar(scalars, MAX_CONCURRENT_WINDOWS, countsObject.maxWindows,
               "The maximum window count must match the expected value.");
@@ -179,18 +178,17 @@ add_task(function* test_subsessionSplit(
 });
 
 add_task(function* test_URIAndDomainCounts() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
 
   let checkCounts = (countsObject) => {
     // Get a snapshot of the scalars and then clear them.
-    const scalars =
-      Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
     checkScalar(scalars, TOTAL_URI_COUNT, countsObject.totalURIs,
                 "The URI scalar must contain the expected value.");
     checkScalar(scalars, UNIQUE_DOMAINS_COUNT, countsObject.domainCount,
                 "The unique domains scalar must contain the expected value.");
     checkScalar(scalars, UNFILTERED_URI_COUNT, countsObject.totalUnfilteredURIs,
                 "The unfiltered URI scalar must contain the expected value.");
   };
 
--- a/browser/modules/test/browser_UsageTelemetry_content.js
+++ b/browser/modules/test/browser_UsageTelemetry_content.js
@@ -66,18 +66,17 @@ add_task(function* test_context_menu() {
                                            gBrowser.selectedBrowser);
   yield popupPromise;
 
   info("Click on search.");
   let searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
   searchItem.click();
 
   info("Validate the search metrics.");
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_CONTEXT_MENU, "search", 1);
   Assert.equal(Object.keys(scalars[SCALAR_CONTEXT_MENU]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.contextmenu', 1);
 
   // Also check events.
@@ -103,18 +102,17 @@ add_task(function* test_about_newtab() {
 
   info("Trigger a simple serch, just text + enter.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield typeInSearchField(tab.linkedBrowser, "test query", "newtab-search-text");
   yield BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser);
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_ABOUT_NEWTAB, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_ABOUT_NEWTAB]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.newtab', 1);
 
   // Also check events.
--- a/browser/modules/test/browser_UsageTelemetry_content_aboutHome.js
+++ b/browser/modules/test/browser_UsageTelemetry_content_aboutHome.js
@@ -66,18 +66,17 @@ add_task(function* test_abouthome_simple
 
   info("Trigger a simple serch, just test + enter.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield typeInSearchField(tab.linkedBrowser, "test query", "searchText");
   yield BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser);
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_ABOUT_HOME, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_ABOUT_HOME]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.abouthome', 1);
 
   // Also check events.
--- a/browser/modules/test/browser_UsageTelemetry_private_and_restore.js
+++ b/browser/modules/test/browser_UsageTelemetry_private_and_restore.js
@@ -22,18 +22,17 @@ add_task(function* test_privateMode() {
   Services.telemetry.clearScalars();
 
   // Open a private window and load a website in it.
   let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private: true});
   yield BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, "http://example.com/");
   yield BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
 
   // Check that tab and window count is recorded.
-  const scalars =
-    Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs in private mode.");
   ok(!(UNFILTERED_URI_COUNT in scalars), "We should not track URIs in private mode.");
   ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains in private mode.");
   is(scalars[TAB_EVENT_COUNT], 1, "The number of open tab event count must match the expected value.");
   is(scalars[MAX_CONCURRENT_TABS], 2, "The maximum tab count must match the expected value.");
   is(scalars[WINDOW_OPEN_COUNT], 1, "The number of window open event count must match the expected value.");
   is(scalars[MAX_CONCURRENT_WINDOWS], 2, "The maximum window count must match the expected value.");
@@ -71,18 +70,17 @@ add_task(function* test_sessionRestore()
 
   // Load the custom state and wait for SSTabRestored, as we want to make sure
   // that the URI counting code was hit.
   let tabRestored = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "SSTabRestored");
   SessionStore.setBrowserState(JSON.stringify(state));
   yield tabRestored;
 
   // Check that the URI is not recorded.
-  const scalars =
-    Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs from restored sessions.");
   ok(!(UNFILTERED_URI_COUNT in scalars), "We should not track URIs from restored sessions.");
   ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains from restored sessions.");
 
   // Restore the original session and cleanup.
   let sessionRestored = promiseBrowserStateRestored();
   SessionStore.setBrowserState(JSON.stringify(state));
--- a/browser/modules/test/browser_UsageTelemetry_searchbar.js
+++ b/browser/modules/test/browser_UsageTelemetry_searchbar.js
@@ -92,18 +92,17 @@ add_task(function* test_plainQuery() {
 
   info("Simulate entering a simple search.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInSearchbar("simple query");
   EventUtils.sendKey("return");
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_SEARCHBAR, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.searchbar', 1);
 
   // Also check events.
@@ -127,18 +126,17 @@ add_task(function* test_oneOff() {
   yield searchInSearchbar("query");
 
   info("Pressing Alt+Down to highlight the first one off engine.");
   EventUtils.synthesizeKey("VK_DOWN", { altKey: true });
   EventUtils.sendKey("return");
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_SEARCHBAR, "search_oneoff", 1);
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch2.searchbar', 1);
 
   // Also check events.
@@ -173,18 +171,17 @@ add_task(function* test_suggestion() {
   info("Perform a one-off search using the first engine.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInSearchbar("query");
   info("Clicking the searchbar suggestion.");
   clickSearchbarSuggestion("queryfoo");
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_SEARCHBAR, "search_suggestion", 1);
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   let searchEngineId = 'other-' + suggestionEngine.name;
   checkKeyedHistogram(search_hist, searchEngineId + '.searchbar', 1);
 
--- a/browser/modules/test/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser_UsageTelemetry_urlbar.js
@@ -111,18 +111,17 @@ add_task(function* test_simpleQuery() {
 
   info("Simulate entering a simple search.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInAwesomebar("simple query");
   EventUtils.sendKey("return");
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1);
 
   // Also check events.
@@ -157,18 +156,17 @@ add_task(function* test_searchAlias() {
 
   info("Search using a search alias.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInAwesomebar("mozalias query");
   EventUtils.sendKey("return");
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_alias", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1);
 
   // Also check events.
@@ -206,18 +204,17 @@ add_task(function* test_oneOff() {
   yield searchInAwesomebar("query");
 
   info("Pressing Alt+Down to take us to the first one-off engine.");
   EventUtils.synthesizeKey("VK_DOWN", { altKey: true });
   EventUtils.sendKey("return");
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_oneoff", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1);
 
   // Also check events.
@@ -266,18 +263,17 @@ add_task(function* test_suggestion() {
   info("Perform a one-off search using the first engine.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInAwesomebar("query");
   info("Clicking the urlbar suggestion.");
   clickURLBarSuggestion("queryfoo");
   yield p;
 
   // Check if the scalars contain the expected values.
-  const scalars =
-    Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_suggestion", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   let searchEngineId = 'other-' + suggestionEngine.name;
   checkKeyedHistogram(search_hist, searchEngineId + '.urlbar', 1);
 
--- a/browser/modules/test/head.js
+++ b/browser/modules/test/head.js
@@ -90,16 +90,26 @@ function getSearchCountsHistogram() {
  * Check that the keyed histogram contains the right value.
  */
 function checkKeyedHistogram(h, key, expectedValue) {
   const snapshot = h.snapshot();
   Assert.ok(key in snapshot, `The histogram must contain ${key}.`);
   Assert.equal(snapshot[key].sum, expectedValue, `The key ${key} must contain ${expectedValue}.`);
 }
 
+/**
+ * Return the scalars from the parent-process.
+ */
+function getParentProcessScalars(aChannel, aKeyed = false, aClear = false) {
+  const scalars = aKeyed ?
+    Services.telemetry.snapshotKeyedScalars(aChannel, aClear)["default"] :
+    Services.telemetry.snapshotScalars(aChannel, aClear)["default"];
+  return scalars || {};
+}
+
 function checkEvents(events, expectedEvents) {
   if (!Services.telemetry.canRecordExtended) {
     // Currently we only collect the tested events when extended Telemetry is enabled.
     return;
   }
 
   Assert.equal(events.length, expectedEvents.length, "Should have matching amount of events.");
 
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -279,8 +279,44 @@ telemetry.test:
     bug_numbers:
       - 1277806
     description: A testing keyed boolean scalar; not meant to be touched.
     expires: never
     kind: boolean
     keyed: true
     notification_emails:
       - telemetry-client-dev@mozilla.com
+    record_in_processes:
+      - 'main'
+      - 'content'
+
+  content_only_uint:
+    bug_numbers:
+      - 1278556
+    description: A testing uint scalar; not meant to be touched.
+    expires: never
+    kind: uint
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    record_in_processes:
+      - 'content'
+
+  all_processes_uint:
+    bug_numbers:
+      - 1278556
+    description: A testing uint scalar; not meant to be touched.
+    expires: never
+    kind: uint
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    record_in_processes:
+      - 'all'
+
+  all_child_processes_string:
+    bug_numbers:
+      - 1278556
+    description: A testing string scalar; not meant to be touched.
+    expires: never
+    kind: string
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    record_in_processes:
+      - 'all_childs'
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -42,16 +42,22 @@ const REASON_ENVIRONMENT_CHANGE = "envir
 const REASON_SHUTDOWN = "shutdown";
 
 const HISTOGRAM_SUFFIXES = {
   PARENT: "",
   CONTENT: "#content",
   GPU: "#gpu",
 }
 
+const INTERNAL_PROCESSES_NAMES = {
+  PARENT: "default",
+  CONTENT: "tab",
+  GPU: "gpu",
+}
+
 const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange";
 
 const MS_IN_ONE_HOUR  = 60 * 60 * 1000;
 const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessionLength", 10 * 60) * 1000;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
 
@@ -973,17 +979,19 @@ var Impl = {
     return ret;
   },
 
   /**
    * Get a snapshot of the scalars and clear them.
    * @param {subsession} If true, then we collect the data for a subsession.
    * @param {clearSubsession} If true, we  need to clear the subsession.
    * @param {keyed} Take a snapshot of keyed or non keyed scalars.
-   * @return {Object} The scalar data as a Javascript object.
+   * @return {Object} The scalar data as a Javascript object, including the
+   *         data from child processes, in the following format:
+   *            {'content': { 'scalarName': ... }, 'gpu': { ... } }
    */
   getScalars(subsession, clearSubsession, keyed) {
     this._log.trace("getScalars - subsession: " + subsession + ", clearSubsession: " +
                     clearSubsession + ", keyed: " + keyed);
 
     if (!subsession) {
       // We only support scalars for subsessions.
       this._log.trace("getScalars - We only support scalars in subsessions.");
@@ -991,21 +999,27 @@ var Impl = {
     }
 
     let scalarsSnapshot = keyed ?
       Telemetry.snapshotKeyedScalars(this.getDatasetType(), clearSubsession) :
       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];
+    for (let processName in scalarsSnapshot) {
+      for (let name in scalarsSnapshot[processName]) {
+        if (name.startsWith('telemetry.test') && this._testing == false) {
+          this._log.trace("getScalars - Skipping test scalar: " + name);
+          continue;
+        }
+        // Finally arrange the data in the returned object.
+        if (!(processName in ret)) {
+          ret[processName] = {};
+        }
+        ret[processName][name] = scalarsSnapshot[processName][name];
       }
     }
 
     return ret;
   },
 
   getEvents(isSubsession, clearSubsession) {
     if (!isSubsession) {
@@ -1282,34 +1296,43 @@ var Impl = {
 
     if (Utils.isContentProcess) {
       return payloadObj;
     }
 
     // Additional payload for chrome process.
     let histograms = protect(() => this.getHistograms(isSubsession, clearSubsession), {});
     let keyedHistograms = protect(() => this.getKeyedHistograms(isSubsession, clearSubsession), {});
+    let scalars = protect(() => this.getScalars(isSubsession, clearSubsession), {});
+    let keyedScalars = protect(() => this.getScalars(isSubsession, clearSubsession, true), {});
+
     payloadObj.histograms = histograms[HISTOGRAM_SUFFIXES.PARENT] || {};
     payloadObj.keyedHistograms = keyedHistograms[HISTOGRAM_SUFFIXES.PARENT] || {};
     payloadObj.processes = {
       parent: {
-        scalars: protect(() => this.getScalars(isSubsession, clearSubsession)),
-        keyedScalars: protect(() => this.getScalars(isSubsession, clearSubsession, true)),
+        scalars: scalars[INTERNAL_PROCESSES_NAMES.PARENT] || {},
+        keyedScalars: keyedScalars[INTERNAL_PROCESSES_NAMES.PARENT] || {},
         events: protect(() => this.getEvents(isSubsession, clearSubsession)),
       },
       content: {
+        scalars: scalars[INTERNAL_PROCESSES_NAMES.CONTENT],
+        keyedScalars: keyedScalars[INTERNAL_PROCESSES_NAMES.CONTENT],
         histograms: histograms[HISTOGRAM_SUFFIXES.CONTENT],
         keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.CONTENT],
       },
     };
 
     // Only include the GPU process if we've accumulated data for it.
     if (HISTOGRAM_SUFFIXES.GPU in histograms ||
-        HISTOGRAM_SUFFIXES.GPU in keyedHistograms) {
+        HISTOGRAM_SUFFIXES.GPU in keyedHistograms ||
+        INTERNAL_PROCESSES_NAMES.GPU in scalars ||
+        INTERNAL_PROCESSES_NAMES.GPU in keyedScalars) {
       payloadObj.processes.gpu = {
+        scalars: scalars[INTERNAL_PROCESSES_NAMES.GPU],
+        keyedScalars: keyedScalars[INTERNAL_PROCESSES_NAMES.GPU],
         histograms: histograms[HISTOGRAM_SUFFIXES.GPU],
         keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.GPU],
       };
     }
 
     payloadObj.info = info;
 
     // Add extended set measurements for chrome process.
--- a/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
+++ b/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
@@ -116,17 +116,24 @@ GetScalarsSnapshot(bool aKeyed, JSContex
     rv = telemetry->SnapshotScalars(nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN,
                                     false, aCx, 0, &scalarsSnapshot);
   }
 
   // Validate the snapshot.
   ASSERT_EQ(rv, NS_OK) << "Creating a snapshot of the data must not fail.";
   ASSERT_TRUE(scalarsSnapshot.isObject()) << "The snapshot must be an object.";
 
-  aResult.set(scalarsSnapshot);
+  // We currently only support scalars from the parent process in the gtests.
+  JS::RootedValue parentScalars(aCx);
+  JS::RootedObject scalarObj(aCx, &scalarsSnapshot.toObject());
+  // Don't complain if no scalars for the parent process can be found. Just
+  // return an empty object.
+  Unused << JS_GetProperty(aCx, scalarObj, "default", &parentScalars);
+
+  aResult.set(parentScalars);
 }
 
 } // Anonymous namespace.
 
 // Test that we can properly write unsigned scalars using the C++ API.
 TEST_F(TelemetryTestFixture, ScalarUnsigned) {
   AutoJSContextWithGlobal cx(mCleanGlobal);
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_ChildScalars.js
@@ -0,0 +1,174 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/TelemetryController.jsm", this);
+Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
+Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
+Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
+
+const MESSAGE_CHILD_TEST_DONE = "ChildTest:Done";
+
+const PLATFORM_VERSION = "1.9.2";
+const APP_VERSION = "1";
+const APP_ID = "xpcshell@tests.mozilla.org";
+const APP_NAME = "XPCShell";
+
+const UINT_SCALAR = "telemetry.test.unsigned_int_kind";
+const KEYED_UINT_SCALAR = "telemetry.test.keyed_unsigned_int";
+const KEYED_BOOL_SCALAR = "telemetry.test.keyed_boolean_kind";
+const CONTENT_ONLY_UINT_SCALAR = "telemetry.test.content_only_uint";
+const ALL_PROCESSES_UINT_SCALAR = "telemetry.test.all_processes_uint";
+const ALL_CHILD_PROCESSES_STRING_SCALAR = "telemetry.test.all_child_processes_string";
+
+function run_child_test() {
+  // Attempt to set some scalar values from the "content" process.
+  // The next scalars are not allowed to be recorded in the content process.
+  Telemetry.scalarSet(UINT_SCALAR, 1);
+  Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, "should-not-be-recorded", 1);
+
+  // The next scalars shou be recorded in only the content process.
+  Telemetry.scalarSet(CONTENT_ONLY_UINT_SCALAR, 37);
+  Telemetry.scalarSet(ALL_CHILD_PROCESSES_STRING_SCALAR, "all-child-processes");
+
+  // The next scalar will be recorded in the parent and content processes.
+  Telemetry.keyedScalarSet(KEYED_BOOL_SCALAR, "content-key", true);
+  Telemetry.keyedScalarSet(KEYED_BOOL_SCALAR, "content-key2", false);
+  Telemetry.scalarSet(ALL_PROCESSES_UINT_SCALAR, 37);
+}
+
+function setParentScalars() {
+  // The following scalars are not allowed to be recorded in the parent process.
+  Telemetry.scalarSet(CONTENT_ONLY_UINT_SCALAR, 15);
+  Telemetry.scalarSet(ALL_CHILD_PROCESSES_STRING_SCALAR, "all-child-processes");
+
+  // The next ones will be recorded only in the parent.
+  Telemetry.scalarSet(UINT_SCALAR, 15);
+
+  // This last batch will be available both in the parent and child processes.
+  Telemetry.keyedScalarSet(KEYED_BOOL_SCALAR, "parent-key", false);
+  Telemetry.scalarSet(ALL_PROCESSES_UINT_SCALAR, 37);
+}
+
+function checkParentScalars(processData) {
+  const scalars = processData.scalars;
+  const keyedScalars = processData.keyedScalars;
+
+  // Check the plain scalars, make sure we're only recording what we expect.
+  Assert.ok(!(CONTENT_ONLY_UINT_SCALAR in scalars),
+            "Scalars must not be recorded in other processes unless allowed.");
+  Assert.ok(!(ALL_CHILD_PROCESSES_STRING_SCALAR in scalars),
+            "Scalars must not be recorded in other processes unless allowed.");
+  Assert.ok(UINT_SCALAR in scalars,
+            `${UINT_SCALAR} must be recorded in the parent process.`);
+  Assert.equal(scalars[UINT_SCALAR], 15,
+               `${UINT_SCALAR} must have the correct value (parent process).`);
+  Assert.ok(ALL_PROCESSES_UINT_SCALAR in scalars,
+            `${ALL_PROCESSES_UINT_SCALAR} must be recorded in the parent process.`);
+  Assert.equal(scalars[ALL_PROCESSES_UINT_SCALAR], 37,
+               `${ALL_PROCESSES_UINT_SCALAR} must have the correct value (parent process).`);
+
+  // Now check the keyed scalars.
+  Assert.ok(KEYED_BOOL_SCALAR in keyedScalars,
+            `${KEYED_BOOL_SCALAR} must be recorded in the parent process.`);
+  Assert.ok('parent-key' in keyedScalars[KEYED_BOOL_SCALAR],
+            `${KEYED_BOOL_SCALAR} must be recorded in the parent process.`);
+  Assert.equal(Object.keys(keyedScalars[KEYED_BOOL_SCALAR]).length, 1,
+            `${KEYED_BOOL_SCALAR} must only contain the expected key in parent process.`);
+  Assert.equal(keyedScalars[KEYED_BOOL_SCALAR]['parent-key'], false,
+            `${KEYED_BOOL_SCALAR} must have the correct value (parent process).`);
+}
+
+function checkContentScalars(processData) {
+  const scalars = processData.scalars;
+  const keyedScalars = processData.keyedScalars;
+
+  // Check the plain scalars for the content process.
+  Assert.ok(!(UINT_SCALAR in scalars),
+            "Scalars must not be recorded in other processes unless allowed.");
+  Assert.ok(!(KEYED_UINT_SCALAR in keyedScalars),
+            "Keyed scalars must not be recorded in other processes unless allowed.");
+  Assert.ok(CONTENT_ONLY_UINT_SCALAR in scalars,
+            `${CONTENT_ONLY_UINT_SCALAR} must be recorded in the content process.`);
+  Assert.equal(scalars[CONTENT_ONLY_UINT_SCALAR], 37,
+            `${CONTENT_ONLY_UINT_SCALAR} must have the correct value (content process).`);
+  Assert.ok(ALL_CHILD_PROCESSES_STRING_SCALAR in scalars,
+            `${ALL_CHILD_PROCESSES_STRING_SCALAR} must be recorded in the content process.`);
+  Assert.equal(scalars[ALL_CHILD_PROCESSES_STRING_SCALAR], "all-child-processes",
+            `${ALL_CHILD_PROCESSES_STRING_SCALAR} must have the correct value (content process).`);
+  Assert.ok(ALL_PROCESSES_UINT_SCALAR in scalars,
+            `${ALL_PROCESSES_UINT_SCALAR} must be recorded in the content process.`);
+  Assert.equal(scalars[ALL_PROCESSES_UINT_SCALAR], 37,
+            `${ALL_PROCESSES_UINT_SCALAR} must have the correct value (content process).`);
+
+  // Check the keyed scalars.
+  Assert.ok(KEYED_BOOL_SCALAR in keyedScalars,
+            `${KEYED_BOOL_SCALAR} must be recorded in the content process.`);
+  Assert.ok('content-key' in keyedScalars[KEYED_BOOL_SCALAR],
+            `${KEYED_BOOL_SCALAR} must be recorded in the content process.`);
+  Assert.ok('content-key2' in keyedScalars[KEYED_BOOL_SCALAR],
+            `${KEYED_BOOL_SCALAR} must be recorded in the content process.`);
+  Assert.equal(keyedScalars[KEYED_BOOL_SCALAR]['content-key'], true,
+            `${KEYED_BOOL_SCALAR} must have the correct value (content process).`);
+  Assert.equal(keyedScalars[KEYED_BOOL_SCALAR]['content-key2'], false,
+            `${KEYED_BOOL_SCALAR} must have the correct value (content process).`);
+  Assert.equal(Object.keys(keyedScalars[KEYED_BOOL_SCALAR]).length, 2,
+            `${KEYED_BOOL_SCALAR} must contain the expected keys in content process.`);
+}
+
+/**
+ * This function waits until content scalars are reported into the
+ * scalar snapshot.
+ */
+function* waitForContentScalars() {
+  yield ContentTaskUtils.waitForCondition(() => {
+    const scalars =
+      Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    return Object.keys(scalars).includes("tab");
+  });
+}
+
+add_task(function*() {
+  if (!runningInParent) {
+    TelemetryController.testSetupContent();
+    run_child_test();
+    do_send_remote_message(MESSAGE_CHILD_TEST_DONE);
+    return;
+  }
+
+  // Setup.
+  do_get_profile(true);
+  loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
+  Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
+  yield TelemetryController.testSetup();
+  if (runningInParent) {
+    setParentScalars();
+    // Make sure we don't generate unexpected pings due to pref changes.
+    yield setEmptyPrefWatchlist();
+  }
+
+  // Run test in child, don't wait for it to finish: just wait for the
+  // MESSAGE_CHILD_TEST_DONE.
+  run_test_in_child("test_ChildScalars.js");
+  yield do_await_remote_message(MESSAGE_CHILD_TEST_DONE);
+
+  // Once scalars are set by the content process, they don't immediately get
+  // sent to the parent process. Wait for the Telemetry IPC Timer to trigger
+  // and batch send the data back to the parent process.
+  yield waitForContentScalars();
+
+  // Get an "environment-changed" ping rather than a "test-ping", as
+  // scalar measurements are only supported in subsession pings.
+  const payload = TelemetrySession.getPayload("environment-change");
+
+  // Validate the scalar data.
+  Assert.ok("processes" in payload, "Should have processes section");
+  Assert.ok("content" in payload.processes, "Should have child process section");
+  Assert.ok("scalars" in payload.processes.content, "Child process section should have scalars.");
+  Assert.ok("keyedScalars" in payload.processes.content, "Child process section should have keyed scalars.");
+  checkParentScalars(payload.processes.parent);
+  checkContentScalars(payload.processes.content);
+
+  do_test_finished();
+});
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js
@@ -2,30 +2,37 @@
    http://creativecommons.org/publicdomain/zero/1.0/
 */
 
 const UINT_SCALAR = "telemetry.test.unsigned_int_kind";
 const STRING_SCALAR = "telemetry.test.string_kind";
 const BOOLEAN_SCALAR = "telemetry.test.boolean_kind";
 const KEYED_UINT_SCALAR = "telemetry.test.keyed_unsigned_int";
 
+function getParentProcessScalars(aChannel, aKeyed = false, aClear = false) {
+  const scalars = aKeyed ?
+    Telemetry.snapshotKeyedScalars(aChannel, aClear)["default"] :
+    Telemetry.snapshotScalars(aChannel, aClear)["default"];
+  return scalars || {};
+}
+
 add_task(function* test_serializationFormat() {
   Telemetry.clearScalars();
 
   // Set the scalars to a known value.
   const expectedUint = 3785;
   const expectedString = "some value";
   Telemetry.scalarSet(UINT_SCALAR, expectedUint);
   Telemetry.scalarSet(STRING_SCALAR, expectedString);
   Telemetry.scalarSet(BOOLEAN_SCALAR, true);
   Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, "first_key", 1234);
 
-  // Get a snapshot of the scalars.
+  // Get a snapshot of the scalars for the main process (internally called "default").
   const scalars =
-    Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   // Check that they are serialized to the correct format.
   Assert.equal(typeof(scalars[UINT_SCALAR]), "number",
                UINT_SCALAR + " must be serialized to the correct format.");
   Assert.ok(Number.isInteger(scalars[UINT_SCALAR]),
                UINT_SCALAR + " must be a finite integer.");
   Assert.equal(scalars[UINT_SCALAR], expectedUint,
                UINT_SCALAR + " must have the correct value.");
@@ -50,17 +57,17 @@ add_task(function* test_keyedSerializati
   const expectedOtherValue = 1107;
 
   Telemetry.scalarSet(UINT_SCALAR, expectedUint);
   Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, expectedKey, expectedUint);
   Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, expectedOtherKey, expectedOtherValue);
 
   // Get a snapshot of the scalars.
   const keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
 
   Assert.ok(!(UINT_SCALAR in keyedScalars),
             UINT_SCALAR + " must not be serialized with the keyed scalars.");
   Assert.ok(KEYED_UINT_SCALAR in keyedScalars,
             KEYED_UINT_SCALAR + " must be serialized with the keyed scalars.");
   Assert.equal(Object.keys(keyedScalars[KEYED_UINT_SCALAR]).length, 2,
                "The keyed scalar must contain exactly 2 keys.");
   Assert.ok(expectedKey in keyedScalars[KEYED_UINT_SCALAR],
@@ -98,23 +105,22 @@ add_task(function* test_nonexistingScala
   Assert.throws(() => Telemetry.keyedScalarSet(NON_EXISTING_SCALAR, "some_key", 11715),
                 /NS_ERROR_ILLEGAL_VALUE/,
                 "Setting a non existing keyed scalar must throw.");
   Assert.throws(() => Telemetry.keyedScalarSetMaximum(NON_EXISTING_SCALAR, "some_key", 11715),
                 /NS_ERROR_ILLEGAL_VALUE/,
                 "Setting the maximum of a non keyed existing scalar must throw.");
 
   // Get a snapshot of the scalars.
-  const scalars =
-    Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+  const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   Assert.ok(!(NON_EXISTING_SCALAR in scalars), "The non existing scalar must not be persisted.");
 
   const keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
 
   Assert.ok(!(NON_EXISTING_SCALAR in keyedScalars),
             "The non existing keyed scalar must not be persisted.");
 });
 
 add_task(function* test_expiredScalar() {
   const EXPIRED_SCALAR = "telemetry.test.expired";
   const EXPIRED_KEYED_SCALAR = "telemetry.test.keyed_expired";
@@ -132,31 +138,31 @@ add_task(function* test_expiredScalar() 
   Telemetry.keyedScalarSetMaximum(EXPIRED_KEYED_SCALAR, "some_key", 11715);
 
   // The unexpired scalar has an expiration version, but far away in the future.
   const expectedValue = 11716;
   Telemetry.scalarSet(UNEXPIRED_SCALAR, expectedValue);
 
   // Get a snapshot of the scalars.
   const scalars =
-    Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
   const keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
 
   Assert.ok(!(EXPIRED_SCALAR in scalars), "The expired scalar must not be persisted.");
   Assert.equal(scalars[UNEXPIRED_SCALAR], expectedValue,
                "The unexpired scalar must be persisted with the correct value.");
   Assert.ok(!(EXPIRED_KEYED_SCALAR in keyedScalars),
             "The expired keyed scalar must not be persisted.");
 });
 
 add_task(function* test_unsignedIntScalar() {
   let checkScalar = (expectedValue) => {
     const scalars =
-      Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+      getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
     Assert.equal(scalars[UINT_SCALAR], expectedValue,
                  UINT_SCALAR + " must contain the expected value.");
   };
 
   Telemetry.clearScalars();
 
   // Let's start with an accumulation without a prior set.
   Telemetry.scalarAdd(UINT_SCALAR, 1);
@@ -204,17 +210,17 @@ add_task(function* test_unsignedIntScala
                 "Setting the scalar to an unexpected value type must throw.");
   // The stored value must not be compromised.
   checkScalar(1);
 });
 
 add_task(function* test_stringScalar() {
   let checkExpectedString = (expectedString) => {
     const scalars =
-      Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+      getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
     Assert.equal(scalars[STRING_SCALAR], expectedString,
                  STRING_SCALAR + " must contain the expected string value.");
   };
 
   Telemetry.clearScalars();
 
   // Let's check simple strings...
   let expected = "test string";
@@ -245,17 +251,17 @@ add_task(function* test_stringScalar() {
   const LONG_STRING = "browser.qaxfiuosnzmhlg.rpvxicawolhtvmbkpnludhedobxvkjwqyeyvmv";
   Telemetry.scalarSet(STRING_SCALAR, LONG_STRING);
   checkExpectedString(LONG_STRING.substr(0, 50));
 });
 
 add_task(function* test_booleanScalar() {
   let checkExpectedBool = (expectedBoolean) => {
     const scalars =
-      Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+      getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
     Assert.equal(scalars[BOOLEAN_SCALAR], expectedBoolean,
                  BOOLEAN_SCALAR + " must contain the expected boolean value.");
   };
 
   Telemetry.clearScalars();
 
   // Set a test boolean value.
   let expected = false;
@@ -294,24 +300,24 @@ add_task(function* test_booleanScalar() 
 });
 
 add_task(function* test_scalarRecording() {
   const OPTIN_SCALAR = "telemetry.test.release_optin";
   const OPTOUT_SCALAR = "telemetry.test.release_optout";
 
   let checkValue = (scalarName, expectedValue) => {
     const scalars =
-      Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+      getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
     Assert.equal(scalars[scalarName], expectedValue,
                  scalarName + " must contain the expected value.");
   };
 
   let checkNotSerialized = (scalarName) => {
     const scalars =
-      Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+      getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
     Assert.ok(!(scalarName in scalars), scalarName + " was not recorded.");
   };
 
   Telemetry.canRecordBase = false;
   Telemetry.canRecordExtended = false;
   Telemetry.clearScalars();
 
   // Check that no scalar is recorded if both base and extended recording are off.
@@ -337,24 +343,24 @@ add_task(function* test_scalarRecording(
 
 add_task(function* test_keyedScalarRecording() {
   const OPTIN_SCALAR = "telemetry.test.keyed_release_optin";
   const OPTOUT_SCALAR = "telemetry.test.keyed_release_optout";
   const testKey = "policy_key";
 
   let checkValue = (scalarName, expectedValue) => {
     const scalars =
-      Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+      getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
     Assert.equal(scalars[scalarName][testKey], expectedValue,
                  scalarName + " must contain the expected value.");
   };
 
   let checkNotSerialized = (scalarName) => {
     const scalars =
-      Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+      getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
     Assert.ok(!(scalarName in scalars), scalarName + " was not recorded.");
   };
 
   Telemetry.canRecordBase = false;
   Telemetry.canRecordExtended = false;
   Telemetry.clearScalars();
 
   // Check that no scalar is recorded if both base and extended recording are off.
@@ -384,35 +390,35 @@ add_task(function* test_subsession() {
   // Set the scalars to a known value.
   Telemetry.scalarSet(UINT_SCALAR, 3785);
   Telemetry.scalarSet(STRING_SCALAR, "some value");
   Telemetry.scalarSet(BOOLEAN_SCALAR, false);
   Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, "some_random_key", 12);
 
   // Get a snapshot and reset the subsession. The value we set must be there.
   let scalars =
-    Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false, true);
   let keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, true);
 
   Assert.equal(scalars[UINT_SCALAR], 3785,
                UINT_SCALAR + " must contain the expected value.");
   Assert.equal(scalars[STRING_SCALAR], "some value",
                STRING_SCALAR + " must contain the expected value.");
   Assert.equal(scalars[BOOLEAN_SCALAR], false,
                BOOLEAN_SCALAR + " must contain the expected value.");
   Assert.equal(keyedScalars[KEYED_UINT_SCALAR]["some_random_key"], 12,
                KEYED_UINT_SCALAR + " must contain the expected value.");
 
   // Get a new snapshot and reset the subsession again. Since no new value
   // was set, the scalars should not be reported.
   scalars =
-    Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false, true);
   keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, true);
 
   Assert.ok(!(UINT_SCALAR in scalars), UINT_SCALAR + " must be empty and not reported.");
   Assert.ok(!(STRING_SCALAR in scalars), STRING_SCALAR + " must be empty and not reported.");
   Assert.ok(!(BOOLEAN_SCALAR in scalars), BOOLEAN_SCALAR + " must be empty and not reported.");
   Assert.ok(!(KEYED_UINT_SCALAR in keyedScalars), KEYED_UINT_SCALAR + " must be empty and not reported.");
 });
 
 add_task(function* test_keyed_uint() {
@@ -432,17 +438,17 @@ add_task(function* test_keyed_uint() {
 
   // Use SetMaximum on the third key.
   Telemetry.keyedScalarSetMaximum(KEYED_UINT_SCALAR, KEYS[2], 37);
   expectedValues[2] = 37;
 
   // Get a snapshot of the scalars and make sure the keys contain
   // the correct values.
   const keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
 
   for (let k = 0; k < 3; k++) {
     const keyName = KEYS[k];
     Assert.equal(keyedScalars[KEYED_UINT_SCALAR][keyName], expectedValues[k],
                  KEYED_UINT_SCALAR + "." + keyName + " must contain the correct value.");
   }
 
   // Are we still throwing when doing unsupported things on uint keyed scalars?
@@ -462,28 +468,28 @@ add_task(function* test_keyed_boolean() 
 
   // Set the initial values.
   Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, first_key, true);
   Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, second_key, false);
 
   // Get a snapshot of the scalars and make sure the keys contain
   // the correct values.
   let keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
   Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][first_key], true,
                "The key must contain the expected value.");
   Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][second_key], false,
                "The key must contain the expected value.");
 
   // Now flip the values and make sure we get the expected values back.
   Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, first_key, false);
   Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, second_key, true);
 
   keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
   Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][first_key], false,
                "The key must contain the expected value.");
   Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][second_key], true,
                "The key must contain the expected value.");
 
   // Are we still throwing when doing unsupported things on a boolean keyed scalars?
   // Just test one single unsupported operation, the other are covered in the plain
   // boolean scalar test.
@@ -510,17 +516,17 @@ add_task(function* test_keyed_keys_lengt
                 /NS_ERROR_ILLEGAL_VALUE/,
                 "Using keys longer than 70 characters must throw.");
   Assert.throws(() => Telemetry.keyedScalarSetMaximum(KEYED_UINT_SCALAR, LONG_KEY_STRING, 10),
                 /NS_ERROR_ILLEGAL_VALUE/,
                 "Using keys longer than 70 characters must throw.");
 
   // Make sure the key with the right length contains the expected value.
   let keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
   Assert.equal(Object.keys(keyedScalars[KEYED_UINT_SCALAR]).length, 1,
                "The keyed scalar must contain exactly 1 key.");
   Assert.ok(NORMAL_KEY in keyedScalars[KEYED_UINT_SCALAR],
             "The keyed scalar must contain the expected key.");
   Assert.equal(keyedScalars[KEYED_UINT_SCALAR][NORMAL_KEY], 1,
                "The key must contain the expected value.");
   Assert.ok(!(LONG_KEY_STRING in keyedScalars[KEYED_UINT_SCALAR]),
             "The data for the long key should not have been recorded.");
@@ -552,17 +558,17 @@ add_task(function* test_keyed_max_keys()
                 "Using more than 100 keys must throw.");
   Assert.throws(() => Telemetry.keyedScalarSetMaximum(KEYED_UINT_SCALAR, LAST_KEY_NAME, 10),
                 /NS_ERROR_FAILURE/,
                 "Using more than 100 keys must throw.");
 
   // Make sure all the keys except the last one are available and have the correct
   // values.
   let keyedScalars =
-    Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
 
   // Check that the keyed scalar only contain the first 100 keys.
   const reportedKeysSet = new Set(Object.keys(keyedScalars[KEYED_UINT_SCALAR]));
   Assert.ok([...keyNamesSet].filter(x => reportedKeysSet.has(x)) &&
             [...reportedKeysSet].filter(x => keyNamesSet.has(x)),
             "The keyed scalar must contain all the 100 keys, and drop the others.");
 
   // Check that all the keys recorded the expected values.
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -50,15 +50,17 @@ tags = addons
 [test_TelemetrySession.js]
 tags = addons
 [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"
 tags = addons
+[test_ChildScalars.js]
+skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 [test_TelemetryReportingPolicy.js]
 tags = addons
 [test_TelemetryScalars.js]
 [test_TelemetryTimestamps.js]
 skip-if = toolkit == 'android'
 [test_TelemetryCaptureStack.js]
 [test_TelemetryEvents.js]