Backed out 13 changesets (bug 1366294) for leaking base::Histogram::FactoryGet a=backout CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 19 Jul 2017 12:49:46 -0700
changeset 418465 6fe44a6c4e46426eebee98c121800c0263a06b47
parent 418464 f2d79c13799ab0ca6eeb044aabdf873ac225efd2
child 418466 eb1d92b2b6a4161492561250f51bae5bafeda68a
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1366294
milestone56.0a1
backs outf2d79c13799ab0ca6eeb044aabdf873ac225efd2
b722d638f6ed9d6a04ed4fa8285029c781c843b7
aee317b1445d926a176b31c14ae1e627fc0b57fd
14f9f9521d4f2e30e084233a6a1f8cc1160eaca4
beb5f00c4b195c51fc8cb37ae21068bd21b4138f
72635bc1ba2511e8e9ce7303982e8b89d1d050aa
c387459a798bbc74ace160813e18563aca8ca94e
476daf9a58465e1954419c874148493cbac534cc
e7ce6a06c131dfe37ceea24a3b09a5b5d6475127
1cc736607aab9744c015e9dc0734dc35511db825
175c3ccb00155c5fe5426fbd7b1f0faa70aca11b
96b594816a21fcafb313b710211ccfa6b1a4237e
57400dd449ad5dd77f88054e06c647023eaa3866
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
Backed out 13 changesets (bug 1366294) for leaking base::Histogram::FactoryGet a=backout CLOSED TREE Backed out changeset f2d79c13799a (bug 1366294) Backed out changeset b722d638f6ed (bug 1366294) Backed out changeset aee317b1445d (bug 1366294) Backed out changeset 14f9f9521d4f (bug 1366294) Backed out changeset beb5f00c4b19 (bug 1366294) Backed out changeset 72635bc1ba25 (bug 1366294) Backed out changeset c387459a798b (bug 1366294) Backed out changeset 476daf9a5846 (bug 1366294) Backed out changeset e7ce6a06c131 (bug 1366294) Backed out changeset 1cc736607aab (bug 1366294) Backed out changeset 175c3ccb0015 (bug 1366294) Backed out changeset 96b594816a21 (bug 1366294) Backed out changeset 57400dd449ad (bug 1366294) MozReview-Commit-ID: J6PyVMGwJ74
browser/components/sessionstore/test/browser_sessionStorage_size.js
dom/base/test/browser_use_counters.js
dom/ipc/tests/browser_remote_navigation_delay_telemetry.js
ipc/chromium/src/base/histogram.cc
ipc/chromium/src/base/histogram.h
ipc/chromium/src/base/message_pump_win.cc
js/xpconnect/src/XPCShellImpl.cpp
toolkit/components/osfile/tests/xpcshell/test_telemetry.js
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/Telemetry.h
toolkit/components/telemetry/TelemetryHistogram.cpp
toolkit/components/telemetry/TelemetryHistogram.h
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/telemetry/gen-histogram-data.py
toolkit/components/telemetry/nsITelemetry.idl
toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
--- a/browser/components/sessionstore/test/browser_sessionStorage_size.js
+++ b/browser/components/sessionstore/test/browser_sessionStorage_size.js
@@ -5,46 +5,34 @@
 
 const RAND = Math.random();
 const URL = "http://mochi.test:8888/browser/" +
             "browser/components/sessionstore/test/browser_sessionStorage.html" +
             "?" + RAND;
 
 const OUTER_VALUE = "outer-value-" + RAND;
 
-function getEstimateChars() {
-  let snap;
-  if (gMultiProcessBrowser) {
-    snap = Services.telemetry.histogramSnapshots.content["FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS"];
-  } else {
-    snap = Services.telemetry.histogramSnapshots.parent["FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS"];
-  }
-  if (!snap) {
-    return 0;
-  }
-  return snap.counts[4];
-}
-
 // Test that we record the size of messages.
 add_task(async function test_telemetry() {
   Services.telemetry.canRecordExtended = true;
-
-  let prev = getEstimateChars()
+  let suffix = gMultiProcessBrowser ? "#content" : "";
+  let histogram = Services.telemetry.getHistogramById("FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS" + suffix);
+  let snap1 = histogram.snapshot();
 
   let tab = BrowserTestUtils.addTab(gBrowser, URL);
   let browser = tab.linkedBrowser;
   await promiseBrowserLoaded(browser);
 
   // Flush to make sure we submitted telemetry data.
   await TabStateFlusher.flush(browser);
 
   // There is no good way to make sure that the parent received the histogram entries from the child processes.
   // Let's stick to the ugly, spinning the event loop until we have a good approach (Bug 1357509).
   await BrowserTestUtils.waitForCondition(() => {
-    return getEstimateChars() > prev;
+    return histogram.snapshot().counts[4] > snap1.counts[4];
   });
 
   Assert.ok(true);
   await promiseRemoveTab(tab);
   Services.telemetry.canRecordExtended = false;
 });
 
 // Lower the size limit for DOM Storage content. Check that DOM Storage
--- a/dom/base/test/browser_use_counters.js
+++ b/dom/base/test/browser_use_counters.js
@@ -103,35 +103,25 @@ function waitForPageLoad(browser) {
       }
       addEventListener("load", listener, true);
     });
   });
 }
 
 function grabHistogramsFromContent(use_counter_middlefix, page_before = null) {
   let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
-  let gather = () => {
-    let snapshots;
-    if (Services.appinfo.browserTabsRemoteAutostart) {
-      snapshots = telemetry.histogramSnapshots.content;
-    } else {
-      snapshots = telemetry.histogramSnapshots.parent;
-    }
-    let checkGet = (probe) => {
-      return snapshots[probe] ? snapshots[probe].sum : 0;
-    };
-    return [
-      checkGet("USE_COUNTER2_" + use_counter_middlefix + "_PAGE"),
-      checkGet("USE_COUNTER2_" + use_counter_middlefix + "_DOCUMENT"),
-      checkGet("CONTENT_DOCUMENTS_DESTROYED"),
-      checkGet("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED"),
-    ];
-  };
+  let suffix = Services.appinfo.browserTabsRemoteAutostart ? "#content" : "";
+  let gather = () => [
+    telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_PAGE" + suffix).snapshot().sum,
+    telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_DOCUMENT" + suffix).snapshot().sum,
+    telemetry.getHistogramById("CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum,
+    telemetry.getHistogramById("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum,
+  ];
   return BrowserTestUtils.waitForCondition(() => {
-    return page_before != gather()[0];
+    return page_before != telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_PAGE" + suffix).snapshot().sum;
   }).then(gather, gather);
 }
 
 var check_use_counter_iframe = async function(file, use_counter_middlefix, check_documents=true) {
   info("checking " + file + " with histogram " + use_counter_middlefix);
 
   let newTab = BrowserTestUtils.addTab(gBrowser,  "about:blank");
   gBrowser.selectedTab = newTab;
--- a/dom/ipc/tests/browser_remote_navigation_delay_telemetry.js
+++ b/dom/ipc/tests/browser_remote_navigation_delay_telemetry.js
@@ -8,42 +8,45 @@ add_task(async function test_memory_dist
     return;
   }
 
   await SpecialPowers.pushPrefEnv({set: [["toolkit.telemetry.enabled", true]]});
   let canRecordExtended = Services.telemetry.canRecordExtended;
   Services.telemetry.canRecordExtended = true;
   registerCleanupFunction(() => Services.telemetry.canRecordExtended = canRecordExtended);
 
-  Services.telemetry.snapshotSubsessionKeyedHistograms(true /*clear*/);
+  // Note the #content suffix after the id. This is the only way this API lets us fetch the
+  // histogram entries reported by a content process.
+  let histogram = Services.telemetry.getKeyedHistogramById("FX_TAB_REMOTE_NAVIGATION_DELAY_MS#content");
+  histogram.clear();
 
   // Open a remote page in a new tab to trigger the WebNavigation:LoadURI.
   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
   ok(tab1.linkedBrowser.isRemoteBrowser, "|tab1| should have a remote browser.");
 
   // Open a new tab with about:robots, so it ends up in the parent process with a non-remote browser.
   let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
   ok(!tab2.linkedBrowser.isRemoteBrowser, "|tab2| should have a non-remote browser.");
   // Navigate the tab, so it will change remotness and it triggers the SessionStore:restoreTabContent case.
   await BrowserTestUtils.loadURI(tab2.linkedBrowser, "http://example.com");
   ok(tab2.linkedBrowser.isRemoteBrowser, "|tab2| should have a remote browser by now.");
 
   // There is no good way to make sure that the parent received the histogram entries from the child processes.
   // Let's stick to the ugly, spinning the event loop until we have a good approach (Bug 1357509).
   await BrowserTestUtils.waitForCondition(() => {
-    let s = Services.telemetry.snapshotSubsessionKeyedHistograms().content["FX_TAB_REMOTE_NAVIGATION_DELAY_MS"];
-    return s && "WebNavigation:LoadURI" in s && "SessionStore:restoreTabContent" in s;
+    let s = histogram.snapshot();
+    return "WebNavigation:LoadURI" in s && "SessionStore:restoreTabContent" in s;
   });
 
-  let s = Services.telemetry.snapshotSubsessionKeyedHistograms().content["FX_TAB_REMOTE_NAVIGATION_DELAY_MS"];
+  let s = histogram.snapshot();
   let restoreTabSnapshot = s["SessionStore:restoreTabContent"];
   ok(restoreTabSnapshot.sum > 0, "Zero delay for the restoreTabContent case is unlikely.");
   ok(restoreTabSnapshot.sum < 10000, "More than 10 seconds delay for the restoreTabContent case is unlikely.");
 
   let loadURISnapshot = s["WebNavigation:LoadURI"];
   ok(loadURISnapshot.sum > 0, "Zero delay for the LoadURI case is unlikely.");
   ok(loadURISnapshot.sum < 10000, "More than 10 seconds delay for the LoadURI case is unlikely.");
 
-  Services.telemetry.snapshotSubsessionKeyedHistograms(true /*clear*/);
+  histogram.clear();
 
   await BrowserTestUtils.removeTab(tab2);
   await BrowserTestUtils.removeTab(tab1);
 });
--- a/ipc/chromium/src/base/histogram.cc
+++ b/ipc/chromium/src/base/histogram.cc
@@ -76,42 +76,52 @@ 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0
 0x2d02ef8dL,
 };
 
 typedef Histogram::Count Count;
 
 // static
 const size_t Histogram::kBucketCount_MAX = 16384u;
 
-Histogram* Histogram::FactoryGet(Sample minimum,
+Histogram* Histogram::FactoryGet(const std::string& name,
+                                 Sample minimum,
                                  Sample maximum,
                                  size_t bucket_count,
                                  Flags flags) {
   Histogram* histogram(NULL);
 
   // Defensive code.
   if (minimum < 1)
     minimum = 1;
   if (maximum > kSampleType_MAX - 1)
     maximum = kSampleType_MAX - 1;
 
-  histogram = new Histogram(minimum, maximum, bucket_count);
-  histogram->InitializeBucketRange();
-  histogram->SetFlags(flags);
+  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
+    // Extra variable is not needed... but this keeps this section basically
+    // identical to other derived classes in this file (and compiler will
+    // optimize away the extra variable.
+    Histogram* tentative_histogram =
+        new Histogram(name, minimum, maximum, bucket_count);
+    tentative_histogram->InitializeBucketRange();
+    tentative_histogram->SetFlags(flags);
+    histogram =
+        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+  }
 
   DCHECK_EQ(HISTOGRAM, histogram->histogram_type());
   DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
   return histogram;
 }
 
-Histogram* Histogram::FactoryTimeGet(TimeDelta minimum,
+Histogram* Histogram::FactoryTimeGet(const std::string& name,
+                                     TimeDelta minimum,
                                      TimeDelta maximum,
                                      size_t bucket_count,
                                      Flags flags) {
-  return FactoryGet(minimum.InMilliseconds(), maximum.InMilliseconds(),
+  return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
                     bucket_count, flags);
 }
 
 void Histogram::Add(int value) {
   if (value > kSampleType_MAX - 1)
     value = kSampleType_MAX - 1;
   if (value < 0)
     value = 0;
@@ -145,16 +155,90 @@ void Histogram::Clear() {
   ss.Resize(*this);
   sample_ = ss;
 }
 
 void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
   DCHECK(false);
 }
 
+// The following methods provide a graphical histogram display.
+void Histogram::WriteHTMLGraph(std::string* output) const {
+  // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
+  output->append("<PRE>");
+  WriteAscii(true, "<br>", output);
+  output->append("</PRE>");
+}
+
+void Histogram::WriteAscii(bool graph_it, const std::string& newline,
+                           std::string* output) const {
+  // Get local (stack) copies of all effectively volatile class data so that we
+  // are consistent across our output activities.
+  SampleSet snapshot;
+  SnapshotSample(&snapshot);
+
+  Count sample_count = snapshot.TotalCount();
+
+  WriteAsciiHeader(snapshot, sample_count, output);
+  output->append(newline);
+
+  // Prepare to normalize graphical rendering of bucket contents.
+  double max_size = 0;
+  if (graph_it)
+    max_size = GetPeakBucketSize(snapshot);
+
+  // Calculate space needed to print bucket range numbers.  Leave room to print
+  // nearly the largest bucket range without sliding over the histogram.
+  size_t largest_non_empty_bucket = bucket_count() - 1;
+  while (0 == snapshot.counts(largest_non_empty_bucket)) {
+    if (0 == largest_non_empty_bucket)
+      break;  // All buckets are empty.
+    --largest_non_empty_bucket;
+  }
+
+  // Calculate largest print width needed for any of our bucket range displays.
+  size_t print_width = 1;
+  for (size_t i = 0; i < bucket_count(); ++i) {
+    if (snapshot.counts(i)) {
+      size_t width = GetAsciiBucketRange(i).size() + 1;
+      if (width > print_width)
+        print_width = width;
+    }
+  }
+
+  int64_t remaining = sample_count;
+  int64_t past = 0;
+  // Output the actual histogram graph.
+  for (size_t i = 0; i < bucket_count(); ++i) {
+    Count current = snapshot.counts(i);
+    if (!current && !PrintEmptyBucket(i))
+      continue;
+    remaining -= current;
+    std::string range = GetAsciiBucketRange(i);
+    output->append(range);
+    for (size_t j = 0; range.size() + j < print_width + 1; ++j)
+      output->push_back(' ');
+    if (0 == current &&
+        i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
+      while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
+        ++i;
+      output->append("... ");
+      output->append(newline);
+      continue;  // No reason to plot emptiness.
+    }
+    double current_size = GetBucketSize(current, i);
+    if (graph_it)
+      WriteAsciiBucketGraph(current_size, max_size, output);
+    WriteAsciiBucketContext(past, current, remaining, i, output);
+    output->append(newline);
+    past += current;
+  }
+  DCHECK_EQ(sample_count, past);
+}
+
 //------------------------------------------------------------------------------
 // Methods for the validating a sample and a related histogram.
 //------------------------------------------------------------------------------
 
 Histogram::Inconsistencies
 Histogram::FindCorruption(const SampleSet& snapshot) const
 {
   int inconsistencies = NO_INCONSISTENCIES;
@@ -181,20 +265,22 @@ Histogram::FindCorruption(const SampleSe
     // we'll catch a redundant count that doesn't match the sample count.  We
     // allow for a certain amount of slop before flagging this as an
     // inconsistency.  Even with an inconsistency, we'll snapshot it again (for
     // UMA in about a half hour, so we'll eventually get the data, if it was
     // not the result of a corruption.  If histograms show that 1 is "too tight"
     // then we may try to use 2 or 3 for this slop value.
     const int kCommonRaceBasedCountMismatch = 1;
     if (delta > 0) {
+      UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta);
       if (delta > kCommonRaceBasedCountMismatch)
         inconsistencies |= COUNT_HIGH_ERROR;
     } else {
       DCHECK_GT(0, delta);
+      UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta);
       if (-delta > kCommonRaceBasedCountMismatch)
         inconsistencies |= COUNT_LOW_ERROR;
     }
   }
   return static_cast<Inconsistencies>(inconsistencies);
 }
 
 Histogram::ClassType Histogram::histogram_type() const {
@@ -246,39 +332,51 @@ size_t Histogram::SizeOfIncludingThis(mo
 size_t
 Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
 {
   // We're not allowed to do deep dives into STL data structures.  This
   // is as close as we can get to measuring this array.
   return aMallocSizeOf(&counts_[0]);
 }
 
-Histogram::Histogram(Sample minimum, Sample maximum, size_t bucket_count)
+Histogram::Histogram(const std::string& name, Sample minimum,
+                     Sample maximum, size_t bucket_count)
   : sample_(),
+    histogram_name_(name),
     declared_min_(minimum),
     declared_max_(maximum),
     bucket_count_(bucket_count),
     flags_(kNoFlags),
     ranges_(bucket_count + 1, 0),
-    range_checksum_(0) {
+    range_checksum_(0),
+    recording_enabled_(true) {
   Initialize();
 }
 
-Histogram::Histogram(TimeDelta minimum, TimeDelta maximum, size_t bucket_count)
+Histogram::Histogram(const std::string& name, TimeDelta minimum,
+                     TimeDelta maximum, size_t bucket_count)
   : sample_(),
+    histogram_name_(name),
     declared_min_(static_cast<int> (minimum.InMilliseconds())),
     declared_max_(static_cast<int> (maximum.InMilliseconds())),
     bucket_count_(bucket_count),
     flags_(kNoFlags),
     ranges_(bucket_count + 1, 0),
-    range_checksum_(0) {
+    range_checksum_(0),
+    recording_enabled_(true) {
   Initialize();
 }
 
 Histogram::~Histogram() {
+  if (StatisticsRecorder::dump_on_exit()) {
+    std::string output;
+    WriteAscii(true, "\n", &output);
+    CHROMIUM_LOG(INFO) << output;
+  }
+
   // Just to make sure most derived class did this properly...
   DCHECK(ValidateBucketRanges());
 }
 
 // Calculate what range of values are held in each bucket.
 // We have to be careful that we don't pick a ratio between starting points in
 // consecutive buckets that is sooo small, that the integer bounds are the same
 // (effectively making one bucket get no values).  We need to avoid:
@@ -457,16 +555,67 @@ double Histogram::GetPeakBucketSize(cons
     double current_size
         = GetBucketSize(snapshot.counts(i), i);
     if (current_size > max)
       max = current_size;
   }
   return max;
 }
 
+void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
+                                 Count sample_count,
+                                 std::string* output) const {
+  StringAppendF(output,
+                "Histogram: %s recorded %d samples",
+                histogram_name().c_str(),
+                sample_count);
+  int64_t snapshot_sum = snapshot.sum();
+  if (0 == sample_count) {
+    DCHECK_EQ(snapshot_sum, 0);
+  } else {
+    double average = static_cast<float>(snapshot_sum) / sample_count;
+
+    StringAppendF(output, ", average = %.1f", average);
+  }
+  if (flags_ & ~kHexRangePrintingFlag)
+    StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
+}
+
+void Histogram::WriteAsciiBucketContext(const int64_t past,
+                                        const Count current,
+                                        const int64_t remaining,
+                                        const size_t i,
+                                        std::string* output) const {
+  double scaled_sum = (past + current + remaining) / 100.0;
+  WriteAsciiBucketValue(current, scaled_sum, output);
+  if (0 < i) {
+    double percentage = past / scaled_sum;
+    StringAppendF(output, " {%3.1f%%}", percentage);
+  }
+}
+
+void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum,
+                                      std::string* output) const {
+  StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
+}
+
+void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
+                                      std::string* output) const {
+  const int k_line_length = 72;  // Maximal horizontal width of graph.
+  int x_count = static_cast<int>(k_line_length * (current_size / max_size)
+                                 + 0.5);
+  int x_remainder = k_line_length - x_count;
+
+  while (0 < x_count--)
+    output->append("-");
+  output->append("O");
+  while (0 < x_remainder--)
+    output->append(" ");
+}
+
 //------------------------------------------------------------------------------
 // Methods for the Histogram::SampleSet class
 //------------------------------------------------------------------------------
 
 Histogram::SampleSet::SampleSet()
     : counts_(),
       sum_(0),
       redundant_count_(0) {
@@ -511,43 +660,48 @@ void Histogram::SampleSet::Add(const Sam
 //------------------------------------------------------------------------------
 // LinearHistogram: This histogram uses a traditional set of evenly spaced
 // buckets.
 //------------------------------------------------------------------------------
 
 LinearHistogram::~LinearHistogram() {
 }
 
-Histogram* LinearHistogram::FactoryGet(Sample minimum,
+Histogram* LinearHistogram::FactoryGet(const std::string& name,
+                                       Sample minimum,
                                        Sample maximum,
                                        size_t bucket_count,
                                        Flags flags) {
   Histogram* histogram(NULL);
 
   if (minimum < 1)
     minimum = 1;
   if (maximum > kSampleType_MAX - 1)
     maximum = kSampleType_MAX - 1;
 
-  LinearHistogram* linear_histogram =
-        new LinearHistogram(minimum, maximum, bucket_count);
-  linear_histogram->InitializeBucketRange();
-  linear_histogram->SetFlags(flags);
-  histogram = linear_histogram;
+  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
+    LinearHistogram* tentative_histogram =
+        new LinearHistogram(name, minimum, maximum, bucket_count);
+    tentative_histogram->InitializeBucketRange();
+    tentative_histogram->SetFlags(flags);
+    histogram =
+        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+  }
 
   DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type());
   DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
   return histogram;
 }
 
-Histogram* LinearHistogram::FactoryTimeGet(TimeDelta minimum,
+Histogram* LinearHistogram::FactoryTimeGet(const std::string& name,
+                                           TimeDelta minimum,
                                            TimeDelta maximum,
                                            size_t bucket_count,
                                            Flags flags) {
-  return FactoryGet(minimum.InMilliseconds(), maximum.InMilliseconds(),
+  return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
                     bucket_count, flags);
 }
 
 Histogram::ClassType LinearHistogram::histogram_type() const {
   return LINEAR_HISTOGRAM;
 }
 
 void LinearHistogram::Accumulate(Sample value, Count count, size_t index) {
@@ -556,26 +710,28 @@ void LinearHistogram::Accumulate(Sample 
 
 void LinearHistogram::SetRangeDescriptions(
     const DescriptionPair descriptions[]) {
   for (int i =0; descriptions[i].description; ++i) {
     bucket_description_[descriptions[i].sample] = descriptions[i].description;
   }
 }
 
-LinearHistogram::LinearHistogram(Sample minimum,
+LinearHistogram::LinearHistogram(const std::string& name,
+                                 Sample minimum,
                                  Sample maximum,
                                  size_t bucket_count)
-    : Histogram(minimum >= 1 ? minimum : 1, maximum, bucket_count) {
+    : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) {
 }
 
-LinearHistogram::LinearHistogram(TimeDelta minimum,
+LinearHistogram::LinearHistogram(const std::string& name,
+                                 TimeDelta minimum,
                                  TimeDelta maximum,
                                  size_t bucket_count)
-    : Histogram(minimum >= TimeDelta::FromMilliseconds(1) ?
+    : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
                                  minimum : TimeDelta::FromMilliseconds(1),
                 maximum, bucket_count) {
 }
 
 void LinearHistogram::InitializeBucketRange() {
   DCHECK_GT(declared_min(), 0);  // 0 is the underflow bucket here.
   double min = declared_min();
   double max = declared_max();
@@ -608,69 +764,74 @@ bool LinearHistogram::PrintEmptyBucket(s
   return bucket_description_.find(ranges(index)) == bucket_description_.end();
 }
 
 
 //------------------------------------------------------------------------------
 // This section provides implementation for BooleanHistogram.
 //------------------------------------------------------------------------------
 
-Histogram* BooleanHistogram::FactoryGet(Flags flags) {
+Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) {
   Histogram* histogram(NULL);
 
-  BooleanHistogram* tentative_histogram = new BooleanHistogram();
-  tentative_histogram->InitializeBucketRange();
-  tentative_histogram->SetFlags(flags);
-  histogram = tentative_histogram;
+  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
+    BooleanHistogram* tentative_histogram = new BooleanHistogram(name);
+    tentative_histogram->InitializeBucketRange();
+    tentative_histogram->SetFlags(flags);
+    histogram =
+        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+  }
 
   DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type());
   return histogram;
 }
 
 Histogram::ClassType BooleanHistogram::histogram_type() const {
   return BOOLEAN_HISTOGRAM;
 }
 
 void BooleanHistogram::AddBoolean(bool value) {
   Add(value ? 1 : 0);
 }
 
-BooleanHistogram::BooleanHistogram()
-    : LinearHistogram(1, 2, 3) {
+BooleanHistogram::BooleanHistogram(const std::string& name)
+    : LinearHistogram(name, 1, 2, 3) {
 }
 
 void
 BooleanHistogram::Accumulate(Sample value, Count count, size_t index)
 {
   // Callers will have computed index based on the non-booleanified value.
   // So we need to adjust the index manually.
   LinearHistogram::Accumulate(!!value, count, value ? 1 : 0);
 }
 
 //------------------------------------------------------------------------------
 // FlagHistogram:
 //------------------------------------------------------------------------------
 
 Histogram *
-FlagHistogram::FactoryGet(Flags flags)
+FlagHistogram::FactoryGet(const std::string &name, Flags flags)
 {
   Histogram *h(nullptr);
 
-  FlagHistogram *fh = new FlagHistogram();
-  fh->InitializeBucketRange();
-  fh->SetFlags(flags);
-  size_t zero_index = fh->BucketIndex(0);
-  fh->LinearHistogram::Accumulate(0, 1, zero_index);
-  h = fh;
+  if (!StatisticsRecorder::FindHistogram(name, &h)) {
+    FlagHistogram *fh = new FlagHistogram(name);
+    fh->InitializeBucketRange();
+    fh->SetFlags(flags);
+    size_t zero_index = fh->BucketIndex(0);
+    fh->LinearHistogram::Accumulate(0, 1, zero_index);
+    h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh);
+  }
 
   return h;
 }
 
-FlagHistogram::FlagHistogram()
-  : BooleanHistogram(), mSwitched(false) {
+FlagHistogram::FlagHistogram(const std::string &name)
+  : BooleanHistogram(name), mSwitched(false) {
 }
 
 Histogram::ClassType
 FlagHistogram::histogram_type() const
 {
   return FLAG_HISTOGRAM;
 }
 
@@ -723,30 +884,32 @@ FlagHistogram::Clear() {
   LinearHistogram::Accumulate(0, 1, zero_index);
 }
 
 //------------------------------------------------------------------------------
 // CountHistogram:
 //------------------------------------------------------------------------------
 
 Histogram *
-CountHistogram::FactoryGet(Flags flags)
+CountHistogram::FactoryGet(const std::string &name, Flags flags)
 {
   Histogram *h(nullptr);
 
-  CountHistogram *fh = new CountHistogram();
-  fh->InitializeBucketRange();
-  fh->SetFlags(flags);
-  h = fh;
+  if (!StatisticsRecorder::FindHistogram(name, &h)) {
+    CountHistogram *fh = new CountHistogram(name);
+    fh->InitializeBucketRange();
+    fh->SetFlags(flags);
+    h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh);
+  }
 
   return h;
 }
 
-CountHistogram::CountHistogram()
-  : LinearHistogram(1, 2, 3) {
+CountHistogram::CountHistogram(const std::string &name)
+  : LinearHistogram(name, 1, 2, 3) {
 }
 
 Histogram::ClassType
 CountHistogram::histogram_type() const
 {
   return COUNT_HISTOGRAM;
 }
 
@@ -774,50 +937,55 @@ CountHistogram::AddSampleSet(const Sampl
   }
 }
 
 
 //------------------------------------------------------------------------------
 // CustomHistogram:
 //------------------------------------------------------------------------------
 
-Histogram* CustomHistogram::FactoryGet(const std::vector<Sample>& custom_ranges,
+Histogram* CustomHistogram::FactoryGet(const std::string& name,
+                                       const std::vector<Sample>& custom_ranges,
                                        Flags flags) {
   Histogram* histogram(NULL);
 
   // Remove the duplicates in the custom ranges array.
   std::vector<int> ranges = custom_ranges;
   ranges.push_back(0);  // Ensure we have a zero value.
   std::sort(ranges.begin(), ranges.end());
   ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
   if (ranges.size() <= 1) {
     DCHECK(false);
     // Note that we pushed a 0 in above, so for defensive code....
     ranges.push_back(1);  // Put in some data so we can index to [1].
   }
 
   DCHECK_LT(ranges.back(), kSampleType_MAX);
 
-  CustomHistogram* custom_histogram = new CustomHistogram(ranges);
-  custom_histogram->InitializedCustomBucketRange(ranges);
-  custom_histogram->SetFlags(flags);
-  histogram = custom_histogram;
+  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
+    CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges);
+    tentative_histogram->InitializedCustomBucketRange(ranges);
+    tentative_histogram->SetFlags(flags);
+    histogram =
+        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+  }
 
   DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM);
   DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(),
                                             ranges.size()));
   return histogram;
 }
 
 Histogram::ClassType CustomHistogram::histogram_type() const {
   return CUSTOM_HISTOGRAM;
 }
 
-CustomHistogram::CustomHistogram(const std::vector<Sample>& custom_ranges)
-    : Histogram(custom_ranges[1], custom_ranges.back(),
+CustomHistogram::CustomHistogram(const std::string& name,
+                                 const std::vector<Sample>& custom_ranges)
+    : Histogram(name, custom_ranges[1], custom_ranges.back(),
                 custom_ranges.size()) {
   DCHECK_GT(custom_ranges.size(), 1u);
   DCHECK_EQ(custom_ranges[0], 0);
 }
 
 void CustomHistogram::InitializedCustomBucketRange(
     const std::vector<Sample>& custom_ranges) {
   DCHECK_GT(custom_ranges.size(), 1u);
@@ -827,9 +995,182 @@ void CustomHistogram::InitializedCustomB
     SetBucketRange(index, custom_ranges[index]);
   ResetRangeChecksum();
 }
 
 double CustomHistogram::GetBucketSize(Count current, size_t i) const {
   return 1;
 }
 
+//------------------------------------------------------------------------------
+// The next section handles global (central) support for all histograms, as well
+// as startup/teardown of this service.
+//------------------------------------------------------------------------------
+
+// This singleton instance should be started during the single threaded portion
+// of main(), and hence it is not thread safe.  It initializes globals to
+// provide support for all future calls.
+StatisticsRecorder::StatisticsRecorder() {
+  DCHECK(!histograms_);
+  if (lock_ == NULL) {
+    // This will leak on purpose. It's the only way to make sure we won't race
+    // against the static uninitialization of the module while one of our
+    // static methods relying on the lock get called at an inappropriate time
+    // during the termination phase. Since it's a static data member, we will
+    // leak one per process, which would be similar to the instance allocated
+    // during static initialization and released only on  process termination.
+    lock_ = new base::Lock;
+  }
+  base::AutoLock auto_lock(*lock_);
+  histograms_ = new HistogramMap;
+}
+
+StatisticsRecorder::~StatisticsRecorder() {
+  DCHECK(histograms_ && lock_);
+
+  if (dump_on_exit_) {
+    std::string output;
+    WriteGraph("", &output);
+    CHROMIUM_LOG(INFO) << output;
+  }
+  // Clean up.
+  HistogramMap* histograms = NULL;
+  {
+    base::AutoLock auto_lock(*lock_);
+    histograms = histograms_;
+    histograms_ = NULL;
+    for (HistogramMap::iterator it = histograms->begin();
+         histograms->end() != it;
+         ++it) {
+      // No other clients permanently hold Histogram references, so we
+      // have the only one and it is safe to delete it.
+      delete it->second;
+    }
+  }
+  delete histograms;
+  // We don't delete lock_ on purpose to avoid having to properly protect
+  // against it going away after we checked for NULL in the static methods.
+}
+
+// static
+bool StatisticsRecorder::IsActive() {
+  if (lock_ == NULL)
+    return false;
+  base::AutoLock auto_lock(*lock_);
+  return NULL != histograms_;
+}
+
+Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) {
+  DCHECK(histogram->HasValidRangeChecksum());
+  if (lock_ == NULL)
+    return histogram;
+  base::AutoLock auto_lock(*lock_);
+  if (!histograms_)
+    return histogram;
+  const std::string name = histogram->histogram_name();
+  HistogramMap::iterator it = histograms_->find(name);
+  // Avoid overwriting a previous registration.
+  if (histograms_->end() == it) {
+    (*histograms_)[name] = histogram;
+  } else {
+    delete histogram;  // We already have one by this name.
+    histogram = it->second;
+  }
+  return histogram;
+}
+
+// static
+void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
+                                        std::string* output) {
+  if (!IsActive())
+    return;
+  output->append("<html><head><title>About Histograms");
+  if (!query.empty())
+    output->append(" - " + query);
+  output->append("</title>"
+                 // We'd like the following no-cache... but it doesn't work.
+                 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
+                 "</head><body>");
+
+  Histograms snapshot;
+  GetSnapshot(query, &snapshot);
+  for (Histograms::iterator it = snapshot.begin();
+       it != snapshot.end();
+       ++it) {
+    (*it)->WriteHTMLGraph(output);
+    output->append("<br><hr><br>");
+  }
+  output->append("</body></html>");
+}
+
+// static
+void StatisticsRecorder::WriteGraph(const std::string& query,
+                                    std::string* output) {
+  if (!IsActive())
+    return;
+  if (query.length())
+    StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
+  else
+    output->append("Collections of all histograms\n");
+
+  Histograms snapshot;
+  GetSnapshot(query, &snapshot);
+  for (Histograms::iterator it = snapshot.begin();
+       it != snapshot.end();
+       ++it) {
+    (*it)->WriteAscii(true, "\n", output);
+    output->append("\n");
+  }
+}
+
+// static
+void StatisticsRecorder::GetHistograms(Histograms* output) {
+  if (lock_ == NULL)
+    return;
+  base::AutoLock auto_lock(*lock_);
+  if (!histograms_)
+    return;
+  for (HistogramMap::iterator it = histograms_->begin();
+       histograms_->end() != it;
+       ++it) {
+    DCHECK_EQ(it->first, it->second->histogram_name());
+    output->push_back(it->second);
+  }
+}
+
+bool StatisticsRecorder::FindHistogram(const std::string& name,
+                                       Histogram** histogram) {
+  if (lock_ == NULL)
+    return false;
+  base::AutoLock auto_lock(*lock_);
+  if (!histograms_)
+    return false;
+  HistogramMap::iterator it = histograms_->find(name);
+  if (histograms_->end() == it)
+    return false;
+  *histogram = it->second;
+  return true;
+}
+
+// private static
+void StatisticsRecorder::GetSnapshot(const std::string& query,
+                                     Histograms* snapshot) {
+  if (lock_ == NULL)
+    return;
+  base::AutoLock auto_lock(*lock_);
+  if (!histograms_)
+    return;
+  for (HistogramMap::iterator it = histograms_->begin();
+       histograms_->end() != it;
+       ++it) {
+    if (it->first.find(query) != std::string::npos)
+      snapshot->push_back(it->second);
+  }
+}
+
+// static
+StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
+// static
+base::Lock* StatisticsRecorder::lock_ = NULL;
+// static
+bool StatisticsRecorder::dump_on_exit_ = false;
+
 }  // namespace base
--- a/ipc/chromium/src/base/histogram.h
+++ b/ipc/chromium/src/base/histogram.h
@@ -51,16 +51,216 @@
 #include <vector>
 
 #include "base/time.h"
 #include "base/lock.h"
 
 namespace base {
 
 //------------------------------------------------------------------------------
+// Provide easy general purpose histogram in a macro, just like stats counters.
+// The first four macros use 50 buckets.
+
+#define HISTOGRAM_TIMES(name, sample) HISTOGRAM_CUSTOM_TIMES( \
+    name, sample, base::TimeDelta::FromMilliseconds(1), \
+    base::TimeDelta::FromSeconds(10), 50)
+
+#define HISTOGRAM_COUNTS(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 1000000, 50)
+
+#define HISTOGRAM_COUNTS_100(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 100, 50)
+
+#define HISTOGRAM_COUNTS_10000(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 10000, 50)
+
+#define HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \
+                                            base::Histogram::kNoFlags); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->Add(sample); \
+  } while (0)
+
+#define HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+    HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
+
+// For folks that need real specific times, use this to select a precise range
+// of times you want plotted, and the number of buckets you want used.
+#define HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+                                                base::Histogram::kNoFlags); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->AddTime(sample); \
+  } while (0)
+
+// DO NOT USE THIS.  It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES.
+#define HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+                                                base::Histogram::kNoFlags); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    if ((sample) < (max)) counter->AddTime(sample); \
+  } while (0)
+
+// Support histograming of an enumerated value.  The samples should always be
+// less than boundary_value.
+
+#define HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
+          boundary_value + 1, base::Histogram::kNoFlags); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->Add(sample); \
+  } while (0)
+
+#define HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \
+                                                  base::Histogram::kNoFlags); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->Add(sample); \
+  } while (0)
+
+
+//------------------------------------------------------------------------------
+// Define Debug vs non-debug flavors of macros.
+#ifndef NDEBUG
+
+#define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample)
+#define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample)
+#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) HISTOGRAM_PERCENTAGE(\
+    name, under_one_hundred)
+#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+    HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count)
+#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \
+    HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count)
+#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+    HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count)
+#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+    HISTOGRAM_ENUMERATION(name, sample, boundary_value)
+#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+    HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges)
+
+#else  // NDEBUG
+
+#define DHISTOGRAM_TIMES(name, sample) do {} while (0)
+#define DHISTOGRAM_COUNTS(name, sample) do {} while (0)
+#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) do {} while (0)
+#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+    do {} while (0)
+#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \
+    do {} while (0)
+#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+    do {} while (0)
+#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) do {} while (0)
+#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+    do {} while (0)
+
+#endif  // NDEBUG
+
+//------------------------------------------------------------------------------
+// The following macros provide typical usage scenarios for callers that wish
+// to record histogram data, and have the data submitted/uploaded via UMA.
+// Not all systems support such UMA, but if they do, the following macros
+// should work with the service.
+
+#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+    name, sample, base::TimeDelta::FromMilliseconds(1), \
+    base::TimeDelta::FromSeconds(10), 50)
+
+#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+    name, sample, base::TimeDelta::FromMilliseconds(10), \
+    base::TimeDelta::FromMinutes(3), 50)
+
+// Use this macro when times can routinely be much longer than 10 seconds.
+#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+    name, sample, base::TimeDelta::FromMilliseconds(1), \
+    base::TimeDelta::FromHours(1), 50)
+
+#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+            base::Histogram::kUmaTargetedHistogramFlag); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->AddTime(sample); \
+  } while (0)
+
+// DO NOT USE THIS.  It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES.
+#define UMA_HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+           base::Histogram::kUmaTargetedHistogramFlag); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    if ((sample) < (max)) counter->AddTime(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 1000000, 50)
+
+#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 100, 50)
+
+#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 10000, 50)
+
+#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \
+          base::Histogram::kUmaTargetedHistogramFlag); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->Add(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1000, 500000, 50)
+
+#define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 1000, 50)
+
+#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+    UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
+
+#define UMA_HISTOGRAM_BOOLEAN(name, sample) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::BooleanHistogram::FactoryGet(name, \
+          base::Histogram::kUmaTargetedHistogramFlag); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->AddBoolean(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
+          boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->Add(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \
+    static base::Histogram* counter(NULL); \
+    if (!counter) \
+      counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \
+          base::Histogram::kUmaTargetedHistogramFlag); \
+    DCHECK_EQ(name, counter->histogram_name()); \
+    counter->Add(sample); \
+  } while (0)
+
+//------------------------------------------------------------------------------
 
 class BooleanHistogram;
 class CustomHistogram;
 class Histogram;
 class LinearHistogram;
 
 class Histogram {
  public:
@@ -168,45 +368,56 @@ class Histogram {
     // and also the snapshotting code may asynchronously get a mismatch (though
     // generally either race based mismatch cause is VERY rare).
     int64_t redundant_count_;
   };
 
   //----------------------------------------------------------------------------
   // minimum should start from 1. 0 is invalid as a minimum. 0 is an implicit
   // default underflow bucket.
-  static Histogram* FactoryGet(Sample minimum,
+  static Histogram* FactoryGet(const std::string& name,
+                               Sample minimum,
                                Sample maximum,
                                size_t bucket_count,
                                Flags flags);
-  static Histogram* FactoryTimeGet(base::TimeDelta minimum,
+  static Histogram* FactoryTimeGet(const std::string& name,
+                                   base::TimeDelta minimum,
                                    base::TimeDelta maximum,
                                    size_t bucket_count,
                                    Flags flags);
 
-  virtual ~Histogram();
-
   void Add(int value);
   void Subtract(int value);
 
+  // TODO: Currently recording_enabled_ is not used by any Histogram class, but
+  //       rather examined only by the telemetry code (via IsRecordingEnabled).
+  //       Move handling to Histogram's Add() etc after simplifying Histogram.
+  void SetRecordingEnabled(bool aEnabled) { recording_enabled_ = aEnabled; };
+  bool IsRecordingEnabled() const { return recording_enabled_; };
+
   // This method is an interface, used only by BooleanHistogram.
   virtual void AddBoolean(bool value);
 
   // Accept a TimeDelta to increment.
   void AddTime(TimeDelta time) {
     Add(static_cast<int>(time.InMilliseconds()));
   }
 
   virtual void AddSampleSet(const SampleSet& sample);
 
   virtual void Clear();
 
   // This method is an interface, used only by LinearHistogram.
   virtual void SetRangeDescriptions(const DescriptionPair descriptions[]);
 
+  // The following methods provide graphical histogram displays.
+  void WriteHTMLGraph(std::string* output) const;
+  void WriteAscii(bool graph_it, const std::string& newline,
+                  std::string* output) const;
+
   // Support generic flagging of Histograms.
   // 0x1 Currently used to mark this histogram to be recorded by UMA..
   // 0x8000 means print ranges in hex.
   void SetFlags(Flags flags) { flags_ = static_cast<Flags> (flags_ | flags); }
   void ClearFlags(Flags flags) { flags_ = static_cast<Flags>(flags_ & ~flags); }
   int flags() const { return flags_; }
 
   // Check to see if bucket ranges, counts and tallies in the snapshot are
@@ -215,16 +426,17 @@ class Histogram {
   // a SnapShot process, but should otherwise be false at all times (unless we
   // have memory over-writes, or DRAM failures).
   virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const;
 
   //----------------------------------------------------------------------------
   // Accessors for factory constuction, serialization and testing.
   //----------------------------------------------------------------------------
   virtual ClassType histogram_type() const;
+  const std::string& histogram_name() const { return histogram_name_; }
   Sample declared_min() const { return declared_min_; }
   Sample declared_max() const { return declared_max_; }
   virtual Sample ranges(size_t i) const;
   uint32_t range_checksum() const { return range_checksum_; }
   virtual size_t bucket_count() const;
 
   // Do a safe atomic snapshot of sample data.  The caller is assumed to
   // have exclusive access to the destination, |*sample|, and no locking
@@ -236,18 +448,22 @@ class Histogram {
 
   virtual bool HasConstructorTimeDeltaArguments(TimeDelta minimum,
                                                 TimeDelta maximum,
                                                 size_t bucket_count);
   // Return true iff the range_checksum_ matches current ranges_ vector.
   bool HasValidRangeChecksum() const;
 
  protected:
-  Histogram(Sample minimum, Sample maximum, size_t bucket_count);
-  Histogram(TimeDelta minimum, TimeDelta maximum, size_t bucket_count);
+  Histogram(const std::string& name, Sample minimum,
+            Sample maximum, size_t bucket_count);
+  Histogram(const std::string& name, TimeDelta minimum,
+            TimeDelta maximum, size_t bucket_count);
+
+  virtual ~Histogram();
 
   // Initialize ranges_ mapping.
   void InitializeBucketRange();
 
   // Method to override to skip the display of the i'th bucket if it's empty.
   virtual bool PrintEmptyBucket(size_t index) const;
 
   //----------------------------------------------------------------------------
@@ -283,34 +499,58 @@ class Histogram {
 
   virtual uint32_t CalculateRangeChecksum() const;
 
   // Finally, provide the state that changes with the addition of each new
   // sample.
   SampleSet sample_;
 
  private:
+  friend class StatisticsRecorder;  // To allow it to delete duplicates.
+
   // Post constructor initialization.
   void Initialize();
 
   // Checksum function for accumulating range values into a checksum.
   static uint32_t Crc32(uint32_t sum, Sample range);
 
   //----------------------------------------------------------------------------
   // Helpers for emitting Ascii graphic.  Each method appends data to output.
 
   // Find out how large the (graphically) the largest bucket will appear to be.
   double GetPeakBucketSize(const SampleSet& snapshot) const;
 
+  // Write a common header message describing this histogram.
+  void WriteAsciiHeader(const SampleSet& snapshot,
+                        Count sample_count, std::string* output) const;
+
+  // Write information about previous, current, and next buckets.
+  // Information such as cumulative percentage, etc.
+  void WriteAsciiBucketContext(const int64_t past, const Count current,
+                               const int64_t remaining, const size_t i,
+                               std::string* output) const;
+
+  // Write textual description of the bucket contents (relative to histogram).
+  // Output is the count in the buckets, as well as the percentage.
+  void WriteAsciiBucketValue(Count current, double scaled_sum,
+                             std::string* output) const;
+
+  // Produce actual graph (set of blank vs non blank char's) for a bucket.
+  void WriteAsciiBucketGraph(double current_size, double max_size,
+                             std::string* output) const;
+
   //----------------------------------------------------------------------------
   // Table for generating Crc32 values.
   static const uint32_t kCrcTable[256];
   //----------------------------------------------------------------------------
   // Invariant values set at/near construction time
 
+  // ASCII version of original name given to the constructor.  All identically
+  // named instances will be coalesced cross-project.
+  const std::string histogram_name_;
   Sample declared_min_;  // Less than this goes into counts_[0]
   Sample declared_max_;  // Over this goes into counts_[bucket_count_ - 1].
   size_t bucket_count_;  // Dimension of counts_[].
 
   // Flag the histogram for recording by UMA via metric_services.h.
   Flags flags_;
 
   // For each index, show the least value that can be stored in the
@@ -319,51 +559,58 @@ class Histogram {
   // The dimension of ranges_ is bucket_count + 1.
   Ranges ranges_;
 
   // For redundancy, we store a checksum of all the sample ranges when ranges
   // are generated.  If ever there is ever a difference, then the histogram must
   // have been corrupted.
   uint32_t range_checksum_;
 
+  // When false, new samples are completely ignored.
+  mozilla::Atomic<bool, mozilla::Relaxed> recording_enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(Histogram);
 };
 
 //------------------------------------------------------------------------------
 
 // LinearHistogram is a more traditional histogram, with evenly spaced
 // buckets.
 class LinearHistogram : public Histogram {
  public:
   virtual ~LinearHistogram();
 
   /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit
      default underflow bucket. */
-  static Histogram* FactoryGet(Sample minimum,
+  static Histogram* FactoryGet(const std::string& name,
+                               Sample minimum,
                                Sample maximum,
                                size_t bucket_count,
                                Flags flags);
-  static Histogram* FactoryTimeGet(TimeDelta minimum,
+  static Histogram* FactoryTimeGet(const std::string& name,
+                                   TimeDelta minimum,
                                    TimeDelta maximum,
                                    size_t bucket_count,
                                    Flags flags);
 
   // Overridden from Histogram:
   virtual ClassType histogram_type() const;
 
   virtual void Accumulate(Sample value, Count count, size_t index);
 
   // Store a list of number/text values for use in rendering the histogram.
   // The last element in the array has a null in its "description" slot.
   virtual void SetRangeDescriptions(const DescriptionPair descriptions[]);
 
  protected:
-  LinearHistogram(Sample minimum, Sample maximum, size_t bucket_count);
+  LinearHistogram(const std::string& name, Sample minimum,
+                  Sample maximum, size_t bucket_count);
 
-  LinearHistogram(TimeDelta minimum, TimeDelta maximum, size_t bucket_count);
+  LinearHistogram(const std::string& name, TimeDelta minimum,
+                  TimeDelta maximum, size_t bucket_count);
 
   // Initialize ranges_ mapping.
   void InitializeBucketRange();
   virtual double GetBucketSize(Count current, size_t i) const;
 
   // If we have a description for a bucket, then return that.  Otherwise
   // let parent class provide a (numeric) description.
   virtual const std::string GetAsciiBucketRange(size_t i) const;
@@ -382,88 +629,152 @@ class LinearHistogram : public Histogram
   DISALLOW_COPY_AND_ASSIGN(LinearHistogram);
 };
 
 //------------------------------------------------------------------------------
 
 // BooleanHistogram is a histogram for booleans.
 class BooleanHistogram : public LinearHistogram {
  public:
-  static Histogram* FactoryGet(Flags flags);
+  static Histogram* FactoryGet(const std::string& name, Flags flags);
 
   virtual ClassType histogram_type() const;
 
   virtual void AddBoolean(bool value);
 
   virtual void Accumulate(Sample value, Count count, size_t index);
 
  protected:
-  explicit BooleanHistogram();
+  explicit BooleanHistogram(const std::string& name);
 
   DISALLOW_COPY_AND_ASSIGN(BooleanHistogram);
 };
 
 //------------------------------------------------------------------------------
 
 // FlagHistogram is like boolean histogram, but only allows a single off/on value.
 class FlagHistogram : public BooleanHistogram
 {
 public:
-  static Histogram *FactoryGet(Flags flags);
+  static Histogram *FactoryGet(const std::string &name, Flags flags);
 
   virtual ClassType histogram_type() const;
 
   virtual void Accumulate(Sample value, Count count, size_t index);
 
   virtual void AddSampleSet(const SampleSet& sample);
 
   virtual void Clear();
 
 private:
-  explicit FlagHistogram();
+  explicit FlagHistogram(const std::string &name);
   bool mSwitched;
 
   DISALLOW_COPY_AND_ASSIGN(FlagHistogram);
 };
 
 // CountHistogram only allows a single monotic counter value.
 class CountHistogram : public LinearHistogram
 {
 public:
-  static Histogram *FactoryGet(Flags flags);
+  static Histogram *FactoryGet(const std::string &name, Flags flags);
 
   virtual ClassType histogram_type() const;
 
   virtual void Accumulate(Sample value, Count count, size_t index);
 
   virtual void AddSampleSet(const SampleSet& sample);
 
 private:
-  explicit CountHistogram();
+  explicit CountHistogram(const std::string &name);
 
   DISALLOW_COPY_AND_ASSIGN(CountHistogram);
 };
 
 //------------------------------------------------------------------------------
 
 // CustomHistogram is a histogram for a set of custom integers.
 class CustomHistogram : public Histogram {
  public:
 
-  static Histogram* FactoryGet(const std::vector<Sample>& custom_ranges,
+  static Histogram* FactoryGet(const std::string& name,
+                               const std::vector<Sample>& custom_ranges,
                                Flags flags);
 
   // Overridden from Histogram:
   virtual ClassType histogram_type() const;
 
  protected:
-  explicit CustomHistogram(const std::vector<Sample>& custom_ranges);
+  CustomHistogram(const std::string& name,
+                  const std::vector<Sample>& custom_ranges);
 
   // Initialize ranges_ mapping.
   void InitializedCustomBucketRange(const std::vector<Sample>& custom_ranges);
   virtual double GetBucketSize(Count current, size_t i) const;
 
   DISALLOW_COPY_AND_ASSIGN(CustomHistogram);
 };
 
+//------------------------------------------------------------------------------
+// StatisticsRecorder handles all histograms in the system.  It provides a
+// general place for histograms to register, and supports a global API for
+// accessing (i.e., dumping, or graphing) the data in all the histograms.
+
+class StatisticsRecorder {
+ public:
+  typedef std::vector<Histogram*> Histograms;
+
+  StatisticsRecorder();
+
+  ~StatisticsRecorder();
+
+  // Find out if histograms can now be registered into our list.
+  static bool IsActive();
+
+  // Register, or add a new histogram to the collection of statistics. If an
+  // identically named histogram is already registered, then the argument
+  // |histogram| will deleted.  The returned value is always the registered
+  // histogram (either the argument, or the pre-existing registered histogram).
+  static Histogram* RegisterOrDeleteDuplicate(Histogram* histogram);
+
+  // Methods for printing histograms.  Only histograms which have query as
+  // a substring are written to output (an empty string will process all
+  // registered histograms).
+  static void WriteHTMLGraph(const std::string& query, std::string* output);
+  static void WriteGraph(const std::string& query, std::string* output);
+
+  // Method for extracting histograms which were marked for use by UMA.
+  static void GetHistograms(Histograms* output);
+
+  // Find a histogram by name. It matches the exact name. This method is thread
+  // safe.  If a matching histogram is not found, then the |histogram| is
+  // not changed.
+  static bool FindHistogram(const std::string& query, Histogram** histogram);
+
+  static bool dump_on_exit() { return dump_on_exit_; }
+
+  static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; }
+
+  // GetSnapshot copies some of the pointers to registered histograms into the
+  // caller supplied vector (Histograms).  Only histograms with names matching
+  // query are returned. The query must be a substring of histogram name for its
+  // pointer to be copied.
+  static void GetSnapshot(const std::string& query, Histograms* snapshot);
+
+
+ private:
+  // We keep all registered histograms in a map, from name to histogram.
+  typedef std::map<std::string, Histogram*> HistogramMap;
+
+  static HistogramMap* histograms_;
+
+  // lock protects access to the above map.
+  static Lock* lock_;
+
+  // Dump all known histograms to log.
+  static bool dump_on_exit_;
+
+  DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
+};
+
 }  // namespace base
 
 #endif  // BASE_METRICS_HISTOGRAM_H_
--- a/ipc/chromium/src/base/message_pump_win.cc
+++ b/ipc/chromium/src/base/message_pump_win.cc
@@ -156,16 +156,18 @@ void MessagePumpForUI::PumpOutPendingPai
   for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
     MSG msg;
     if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
       break;
     ProcessMessageHelper(msg);
     if (state_->should_quit)  // Handle WM_QUIT.
       break;
   }
+  // Histogram what was really being used, to help to adjust kMaxPeekCount.
+  DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count);
 }
 
 //-----------------------------------------------------------------------------
 // MessagePumpForUI private:
 
 // static
 LRESULT CALLBACK MessagePumpForUI::WndProcThunk(
     HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -30,16 +30,18 @@
 #include "BackstagePass.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsJSUtils.h"
 #include "gfxPrefs.h"
 #include "nsIXULRuntime.h"
 #include "GeckoProfiler.h"
 
+#include "base/histogram.h"
+
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #ifdef XP_WIN
 #include "mozilla/widget/AudioSession.h"
 #include <windows.h>
 #if defined(MOZ_SANDBOX)
@@ -1218,16 +1220,21 @@ XRE_XPCShellMain(int argc, char** argv, 
     gErrFile = stderr;
     gOutFile = stdout;
     gInFile = stdin;
 
     NS_LogInit();
 
     mozilla::LogModule::Init();
 
+    // A initializer to initialize histogram collection
+    // used by telemetry.
+    auto telStats =
+       mozilla::MakeUnique<base::StatisticsRecorder>();
+
     char aLocal;
     profiler_init(&aLocal);
 
     if (PR_GetEnv("MOZ_CHAOSMODE")) {
         ChaosFeature feature = ChaosFeature::Any;
         long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16);
         if (featureInt) {
             // NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature.
@@ -1547,16 +1554,18 @@ XRE_XPCShellMain(int argc, char** argv, 
 
     if (!XRE_ShutdownTestShell())
         NS_ERROR("problem shutting down testshell");
 
     // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
     rv = NS_ShutdownXPCOM( nullptr );
     MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
 
+    telStats = nullptr;
+
 #ifdef MOZ_CRASHREPORTER
     // Shut down the crashreporter service to prevent leaking some strings it holds.
     if (CrashReporter::GetEnabled())
         CrashReporter::UnsetExceptionHandler();
 #endif
 
     // This must precede NS_LogTerm(), otherwise xpcshell return non-zero
     // during some tests, which causes failures.
--- a/toolkit/components/osfile/tests/xpcshell/test_telemetry.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_telemetry.js
@@ -22,42 +22,42 @@ function getCount(histogram) {
 }
 
 // Ensure that launching the OS.File worker adds data to the relevant
 // histograms
 add_task(async function test_startup() {
   let LAUNCH = "OSFILE_WORKER_LAUNCH_MS";
   let READY = "OSFILE_WORKER_READY_MS";
 
-  let before = Services.telemetry.histogramSnapshots.parent;
+  let before = Services.telemetry.histogramSnapshots;
 
   // Launch the OS.File worker
   await File.getCurrentDirectory();
 
-  let after = Services.telemetry.histogramSnapshots.parent;
+  let after = Services.telemetry.histogramSnapshots;
 
 
   do_print("Ensuring that we have recorded measures for histograms");
   do_check_eq(getCount(after[LAUNCH]), getCount(before[LAUNCH]) + 1);
   do_check_eq(getCount(after[READY]), getCount(before[READY]) + 1);
 
   do_print("Ensuring that launh <= ready");
   do_check_true(after[LAUNCH].sum <= after[READY].sum);
 });
 
 // Ensure that calling writeAtomic adds data to the relevant histograms
 add_task(async function test_writeAtomic() {
   let LABEL = "OSFILE_WRITEATOMIC_JANK_MS";
 
-  let before = Services.telemetry.histogramSnapshots.parent;
+  let before = Services.telemetry.histogramSnapshots;
 
   // Perform a write.
   let path = Path.join(Constants.Path.profileDir, "test_osfile_telemetry.tmp");
   await File.writeAtomic(path, LABEL, { tmpPath: path + ".tmp" } );
 
-  let after = Services.telemetry.histogramSnapshots.parent;
+  let after = Services.telemetry.histogramSnapshots;
 
   do_check_eq(getCount(after[LABEL]), getCount(before[LABEL]) + 1);
 });
 
 function run_test() {
   run_next_test();
 }
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -853,30 +853,17 @@ TelemetryImpl::SnapshotSubsessionHistogr
 #else
   return NS_OK;
 #endif
 }
 
 NS_IMETHODIMP
 TelemetryImpl::GetKeyedHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
-  return TelemetryHistogram::GetKeyedHistogramSnapshots(cx, ret, false, false);
-}
-
-NS_IMETHODIMP
-TelemetryImpl::SnapshotSubsessionKeyedHistograms(bool clearSubsession,
-                                                 JSContext *cx,
-                                                 JS::MutableHandle<JS::Value> ret)
-{
-#if !defined(MOZ_WIDGET_ANDROID)
-  return TelemetryHistogram::GetKeyedHistogramSnapshots(cx, ret, true,
-                                                        clearSubsession);
-#else
-  return NS_OK;
-#endif
+  return TelemetryHistogram::GetKeyedHistogramSnapshots(cx, ret);
 }
 
 bool
 TelemetryImpl::GetSQLStats(JSContext *cx, JS::MutableHandle<JS::Value> ret, bool includePrivateSql)
 {
   JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
   if (!root_obj)
     return false;
@@ -2424,16 +2411,26 @@ SetProfileDir(nsIFile* aProfD)
   nsAutoString profDirPath;
   nsresult rv = aProfD->GetPath(profDirPath);
   if (NS_FAILED(rv)) {
     return;
   }
   sTelemetryIOObserver->AddPath(profDirPath, NS_LITERAL_STRING("{profile}"));
 }
 
+void CreateStatisticsRecorder()
+{
+  TelemetryHistogram::CreateStatisticsRecorder();
+}
+
+void DestroyStatisticsRecorder()
+{
+  TelemetryHistogram::DestroyStatisticsRecorder();
+}
+
 // Scalar API C++ Endpoints
 
 void
 ScalarAdd(mozilla::Telemetry::ScalarID aId, uint32_t aVal)
 {
   TelemetryScalar::Add(aId, aVal);
 }
 
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -41,16 +41,23 @@ struct KeyedScalarAction;
 struct ChildEventData;
 
 enum TimerResolution {
   Millisecond,
   Microsecond
 };
 
 /**
+ * Create and destroy the underlying base::StatisticsRecorder singleton.
+ * Creation has to be done very early in the startup sequence.
+ */
+void CreateStatisticsRecorder();
+void DestroyStatisticsRecorder();
+
+/**
  * Initialize the Telemetry service on the main thread at startup.
  */
 void Init();
 
 /**
  * Adds sample to a histogram defined in TelemetryHistogramEnums.h
  *
  * @param id - histogram id
@@ -138,17 +145,17 @@ void AccumulateCategorical(HistogramID i
  *
  * @param id - histogram id
  * @param start - start time
  * @param end - end time
  */
 void AccumulateTimeDelta(HistogramID id, TimeStamp start, TimeStamp end = TimeStamp::Now());
 
 /**
- * Enable/disable recording for this histogram in this process at runtime.
+ * Enable/disable recording for this histogram at runtime.
  * Recording is enabled by default, unless listed at kRecordingInitiallyDisabledIDs[].
  * id must be a valid telemetry enum, otherwise an assertion is triggered.
  *
  * @param id - histogram id
  * @param enabled - whether or not to enable recording from now on.
  */
 void SetHistogramRecordingEnabled(HistogramID id, bool enabled);
 
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -23,34 +23,28 @@
 
 #include "TelemetryCommon.h"
 #include "TelemetryHistogram.h"
 #include "ipc/TelemetryIPCAccumulator.h"
 
 #include "base/histogram.h"
 
 using base::Histogram;
+using base::StatisticsRecorder;
 using base::BooleanHistogram;
 using base::CountHistogram;
 using base::FlagHistogram;
 using base::LinearHistogram;
 using mozilla::StaticMutex;
 using mozilla::StaticMutexAutoLock;
 using mozilla::Telemetry::Accumulation;
 using mozilla::Telemetry::KeyedAccumulation;
-using mozilla::Telemetry::HistogramID;
 using mozilla::Telemetry::ProcessID;
-using mozilla::Telemetry::HistogramCount;
 using mozilla::Telemetry::Common::LogToBrowserConsole;
 using mozilla::Telemetry::Common::RecordedProcessType;
-using mozilla::Telemetry::Common::AutoHashtable;
-using mozilla::Telemetry::Common::GetNameForProcessID;
-using mozilla::Telemetry::Common::IsExpiredVersion;
-using mozilla::Telemetry::Common::CanRecordDataset;
-using mozilla::Telemetry::Common::IsInDataset;
 
 namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // Naming: there are two kinds of functions in this file:
 //
@@ -100,223 +94,116 @@ namespace TelemetryIPCAccumulator = mozi
 // means that this file is not guaranteed race-free.
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE TYPES
 
+#define EXPIRED_ID "__expired__"
+#define SUBSESSION_HISTOGRAM_PREFIX "sub#"
+#define KEYED_HISTOGRAM_NAME_SEPARATOR "#"
+#define CONTENT_HISTOGRAM_SUFFIX "#content"
+#define GPU_HISTOGRAM_SUFFIX "#gpu"
+#define EXTENSION_HISTOGRAM_SUFFIX "#extension"
+
 namespace {
 
-typedef nsDataHashtable<nsCStringHashKey, HistogramID> StringToHistogramIdMap;
+using mozilla::Telemetry::Common::AutoHashtable;
+using mozilla::Telemetry::Common::IsExpiredVersion;
+using mozilla::Telemetry::Common::CanRecordDataset;
+using mozilla::Telemetry::Common::IsInDataset;
+
+class KeyedHistogram;
+
+typedef nsBaseHashtableET<nsDepCharHashKey, mozilla::Telemetry::HistogramID>
+          CharPtrEntryType;
+
+typedef AutoHashtable<CharPtrEntryType> HistogramMapType;
+
+typedef nsClassHashtable<nsCStringHashKey, KeyedHistogram>
+          KeyedHistogramMapType;
 
 // Hardcoded probes
 struct HistogramInfo {
   uint32_t min;
   uint32_t max;
   uint32_t bucketCount;
   uint32_t histogramType;
-  uint32_t name_offset;
+  uint32_t id_offset;
   uint32_t expiration_offset;
   uint32_t dataset;
   uint32_t label_index;
   uint32_t label_count;
   RecordedProcessType record_in_processes;
   bool keyed;
 
-  const char *name() const;
+  const char *id() const;
   const char *expiration() const;
   nsresult label_id(const char* label, uint32_t* labelId) const;
 };
 
 enum reflectStatus {
   REFLECT_OK,
+  REFLECT_CORRUPT,
   REFLECT_FAILURE
 };
 
-enum class SessionType {
-  Session = 0,
-  Subsession = 1,
-  Count,
-};
-
-class KeyedHistogram {
-public:
-  KeyedHistogram(HistogramID id, const HistogramInfo& info);
-  nsresult GetHistogram(const nsCString& name, Histogram** histogram, bool subsession);
-  Histogram* GetHistogram(const nsCString& name, bool subsession);
-  uint32_t GetHistogramType() const { return mHistogramInfo.histogramType; }
-  nsresult GetJSKeys(JSContext* cx, JS::CallArgs& args);
-  nsresult GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
-                         bool subsession, bool clearSubsession);
-
-  nsresult Add(const nsCString& key, uint32_t aSample, ProcessID aProcessType);
-  void Clear(bool subsession);
-
-  HistogramID GetHistogramID() const { return mId; }
-
-private:
-  typedef nsBaseHashtableET<nsCStringHashKey, Histogram*> KeyedHistogramEntry;
-  typedef AutoHashtable<KeyedHistogramEntry> KeyedHistogramMapType;
-  KeyedHistogramMapType mHistogramMap;
-#if !defined(MOZ_WIDGET_ANDROID)
-  KeyedHistogramMapType mSubsessionMap;
-#endif
-
-  static bool ReflectKeyedHistogram(KeyedHistogramEntry* entry,
-                                    JSContext* cx,
-                                    JS::Handle<JSObject*> obj);
-
-  const HistogramID mId;
-  const HistogramInfo& mHistogramInfo;
-};
+typedef StatisticsRecorder::Histograms::iterator HistogramIterator;
 
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE STATE, SHARED BY ALL THREADS
 
 namespace {
 
 // Set to true once this global state has been initialized
 bool gInitDone = false;
 
-// Whether we are collecting the base, opt-out, Histogram data.
 bool gCanRecordBase = false;
-// Whether we are collecting the extended, opt-in, Histogram data.
 bool gCanRecordExtended = false;
 
-// The storage for actual Histogram instances.
-// We use separate ones for plain and keyed histograms.
-Histogram* gHistogramStorage[HistogramCount][uint32_t(ProcessID::Count)][uint32_t(SessionType::Count)] = {};
-// Keyed histograms internally map string keys to individual Histogram instances.
-// KeyedHistogram keeps track of session & subsession histograms internally.
-KeyedHistogram* gKeyedHistogramStorage[HistogramCount][uint32_t(ProcessID::Count)] = {};
+HistogramMapType gHistogramMap(mozilla::Telemetry::HistogramCount);
 
-// Cache of histogram name to a histogram id.
-StringToHistogramIdMap gNameToHistogramIDMap(HistogramCount);
+KeyedHistogramMapType gKeyedHistograms;
+
+bool gCorruptHistograms[mozilla::Telemetry::HistogramCount];
 
-// To simplify logic below we use a single histogram instance for all expired histograms.
-Histogram* gExpiredHistogram = nullptr;
+// This is for gHistograms, gHistogramStringTable
+#include "TelemetryHistogramData.inc"
 
-// This tracks whether recording is enabled for specific histograms.
-// To utilize C++ initialization rules, we invert the meaning to "disabled".
-bool gHistogramRecordingDisabled[HistogramCount] = {};
-
-// This is for gHistogramInfos, gHistogramStringTable
-#include "TelemetryHistogramData.inc"
+// The singleton StatisticsRecorder object for this process.
+base::StatisticsRecorder* gStatisticsRecorder = nullptr;
 
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE CONSTANTS
 
 namespace {
 
 // List of histogram IDs which should have recording disabled initially.
-const HistogramID kRecordingInitiallyDisabledIDs[] = {
+const mozilla::Telemetry::HistogramID kRecordingInitiallyDisabledIDs[] = {
   mozilla::Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
 
   // The array must not be empty. Leave these item here.
   mozilla::Telemetry::TELEMETRY_TEST_COUNT_INIT_NO_RECORD,
   mozilla::Telemetry::TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD
 };
 
 } // namespace
 
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-//
-// The core storage access functions.
-// They wrap access to the histogram storage and lookup caches.
-
-namespace {
-
-// Factory function for histogram instances.
-Histogram*
-internal_CreateHistogramInstance(const HistogramInfo& info);
-
-bool
-internal_IsHistogramEnumId(HistogramID aID)
-{
-  static_assert(((HistogramID)-1 > 0), "ID should be unsigned.");
-  return aID < HistogramCount;
-}
-
-// Look up a plain histogram by id.
-Histogram*
-internal_GetHistogramById(HistogramID histogramId, ProcessID processId, SessionType sessionType,
-                          bool instantiate = true)
-{
-  MOZ_ASSERT(internal_IsHistogramEnumId(histogramId));
-  MOZ_ASSERT(!gHistogramInfos[histogramId].keyed);
-  MOZ_ASSERT(processId < ProcessID::Count);
-  MOZ_ASSERT(sessionType < SessionType::Count);
-
-  Histogram* h = gHistogramStorage[histogramId][uint32_t(processId)][uint32_t(sessionType)];
-  if (h || !instantiate) {
-    return h;
-  }
-
-  const HistogramInfo& info = gHistogramInfos[histogramId];
-  h = internal_CreateHistogramInstance(info);
-  MOZ_ASSERT(h);
-  gHistogramStorage[histogramId][uint32_t(processId)][uint32_t(sessionType)] = h;
-  return h;
-}
-
-// Look up a keyed histogram by id.
-KeyedHistogram*
-internal_GetKeyedHistogramById(HistogramID histogramId, ProcessID processId,
-                               bool instantiate = true)
-{
-  MOZ_ASSERT(internal_IsHistogramEnumId(histogramId));
-  MOZ_ASSERT(gHistogramInfos[histogramId].keyed);
-  MOZ_ASSERT(processId < ProcessID::Count);
-
-  KeyedHistogram* kh = gKeyedHistogramStorage[histogramId][uint32_t(processId)];
-  if (kh || !instantiate) {
-    return kh;
-  }
-
-  const HistogramInfo& info = gHistogramInfos[histogramId];
-  kh = new KeyedHistogram(histogramId, info);
-  gKeyedHistogramStorage[histogramId][uint32_t(processId)] = kh;
-
-  return kh;
-}
-
-// Look up a histogram id from a histogram name.
-nsresult
-internal_GetHistogramIdByName(const nsACString& name, HistogramID* id)
-{
-  const bool found = gNameToHistogramIDMap.Get(name, id);
-  if (!found) {
-    return NS_ERROR_ILLEGAL_VALUE;
-  }
-
-  return NS_OK;
-}
-
-// Clear a histogram from storage.
-void
-internal_ClearHistogramById(HistogramID histogramId, ProcessID processId, SessionType sessionType)
-{
-  delete gHistogramStorage[histogramId][uint32_t(processId)][uint32_t(sessionType)];
-  gHistogramStorage[histogramId][uint32_t(processId)][uint32_t(sessionType)] = nullptr;
-}
-
-}
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: Misc small helpers
 
 namespace {
 
@@ -325,76 +212,69 @@ internal_CanRecordBase() {
   return gCanRecordBase;
 }
 
 bool
 internal_CanRecordExtended() {
   return gCanRecordExtended;
 }
 
+bool
+internal_IsHistogramEnumId(mozilla::Telemetry::HistogramID aID)
+{
+  static_assert(((mozilla::Telemetry::HistogramID)-1 > 0), "ID should be unsigned.");
+  return aID < mozilla::Telemetry::HistogramCount;
+}
+
 // Note: this is completely unrelated to mozilla::IsEmpty.
 bool
 internal_IsEmpty(const Histogram *h)
 {
   Histogram::SampleSet ss;
   h->SnapshotSample(&ss);
   return ss.counts(0) == 0 && ss.sum() == 0;
 }
 
 bool
-internal_IsExpired(Histogram* h)
-{
-  return h == gExpiredHistogram;
-}
-
-void
-internal_SetHistogramRecordingEnabled(HistogramID id, bool aEnabled)
+internal_IsExpired(const Histogram *histogram)
 {
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
-  gHistogramRecordingDisabled[id] = !aEnabled;
-}
-
-bool
-internal_IsRecordingEnabled(HistogramID id)
-{
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
-  return !gHistogramRecordingDisabled[id];
+  return histogram->histogram_name() == EXPIRED_ID;
 }
 
 nsresult
 internal_GetRegisteredHistogramIds(bool keyed, uint32_t dataset,
                                    uint32_t *aCount, char*** aHistograms)
 {
   nsTArray<char*> collection;
 
-  for (const auto & h : gHistogramInfos) {
+  for (const auto & h : gHistograms) {
     if (IsExpiredVersion(h.expiration()) ||
         h.keyed != keyed ||
         !IsInDataset(h.dataset, dataset)) {
       continue;
     }
 
-    const char* id = h.name();
+    const char* id = h.id();
     const size_t len = strlen(id);
     collection.AppendElement(static_cast<char*>(nsMemory::Clone(id, len+1)));
   }
 
   const size_t bytes = collection.Length() * sizeof(char*);
   char** histograms = static_cast<char**>(moz_xmalloc(bytes));
   memcpy(histograms, collection.Elements(), bytes);
   *aHistograms = histograms;
   *aCount = collection.Length();
 
   return NS_OK;
 }
 
 const char *
-HistogramInfo::name() const
+HistogramInfo::id() const
 {
-  return &gHistogramStringTable[this->name_offset];
+  return &gHistogramStringTable[this->id_offset];
 }
 
 const char *
 HistogramInfo::expiration() const
 {
   return &gHistogramStringTable[this->expiration_offset];
 }
 
@@ -429,119 +309,457 @@ HistogramInfo::label_id(const char* labe
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: Histogram Get, Add, Clone, Clear functions
 
 namespace {
 
 nsresult
-internal_CheckHistogramArguments(const HistogramInfo& info)
+internal_CheckHistogramArguments(uint32_t histogramType,
+                                 uint32_t min, uint32_t max,
+                                 uint32_t bucketCount, bool haveOptArgs)
 {
-  if (info.histogramType != nsITelemetry::HISTOGRAM_BOOLEAN
-      && info.histogramType != nsITelemetry::HISTOGRAM_FLAG
-      && info.histogramType != nsITelemetry::HISTOGRAM_COUNT) {
-    // Sanity checks for histogram parameters.
-    if (info.min >= info.max) {
+  if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN
+      && histogramType != nsITelemetry::HISTOGRAM_FLAG
+      && histogramType != nsITelemetry::HISTOGRAM_COUNT) {
+    // The min, max & bucketCount arguments are not optional for this type.
+    if (!haveOptArgs)
       return NS_ERROR_ILLEGAL_VALUE;
-    }
 
-    if (info.bucketCount <= 2) {
+    // Sanity checks for histogram parameters.
+    if (min >= max)
       return NS_ERROR_ILLEGAL_VALUE;
-    }
 
-    if (info.min < 1) {
+    if (bucketCount <= 2)
       return NS_ERROR_ILLEGAL_VALUE;
-    }
+
+    if (min < 1)
+      return NS_ERROR_ILLEGAL_VALUE;
   }
 
   return NS_OK;
 }
 
-Histogram*
-internal_CreateHistogramInstance(const HistogramInfo& passedInfo)
+/*
+ * min, max & bucketCount are optional for boolean, flag & count histograms.
+ * haveOptArgs has to be set if the caller provides them.
+ */
+nsresult
+internal_HistogramGet(const char *name, const char *expiration,
+                      uint32_t histogramType, uint32_t min, uint32_t max,
+                      uint32_t bucketCount, bool haveOptArgs,
+                      Histogram **result)
 {
-  if (NS_FAILED(internal_CheckHistogramArguments(passedInfo))) {
-    MOZ_ASSERT(false, "Failed histogram argument checks.");
-    return nullptr;
+  nsresult rv = internal_CheckHistogramArguments(histogramType, min, max,
+                                                 bucketCount, haveOptArgs);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  // To keep the code simple we map all the calls to expired histograms to the same histogram instance.
-  // We create that instance lazily when needed.
-  const bool isExpired = IsExpiredVersion(passedInfo.expiration());
-  HistogramInfo info = passedInfo;
-
-  if (isExpired) {
-    if (gExpiredHistogram) {
-      return gExpiredHistogram;
-    }
-
-    info.min = 1;
-    info.max = 2;
-    info.bucketCount = 3;
-    info.histogramType = nsITelemetry::HISTOGRAM_LINEAR;
+  if (IsExpiredVersion(expiration)) {
+    name = EXPIRED_ID;
+    min = 1;
+    max = 2;
+    bucketCount = 3;
+    histogramType = nsITelemetry::HISTOGRAM_LINEAR;
   }
 
-  Histogram::Flags flags = Histogram::kNoFlags;
-  Histogram* h = nullptr;
-  switch (info.histogramType) {
+  switch (histogramType) {
   case nsITelemetry::HISTOGRAM_EXPONENTIAL:
-    h = Histogram::FactoryGet(info.min, info.max, info.bucketCount, flags);
+    *result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
     break;
   case nsITelemetry::HISTOGRAM_LINEAR:
   case nsITelemetry::HISTOGRAM_CATEGORICAL:
-    h = LinearHistogram::FactoryGet(info.min, info.max, info.bucketCount, flags);
+    *result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
     break;
   case nsITelemetry::HISTOGRAM_BOOLEAN:
-    h = BooleanHistogram::FactoryGet(flags);
+    *result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
     break;
   case nsITelemetry::HISTOGRAM_FLAG:
-    h = FlagHistogram::FactoryGet(flags);
+    *result = FlagHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
     break;
   case nsITelemetry::HISTOGRAM_COUNT:
-    h = CountHistogram::FactoryGet(flags);
+    *result = CountHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
     break;
   default:
-    MOZ_ASSERT(false, "Invalid histogram type");
+    NS_ASSERTION(false, "Invalid histogram type");
+    return NS_ERROR_INVALID_ARG;
+  }
+  return NS_OK;
+}
+
+// Read the process type from the given histogram name. The process type, if
+// one exists, is embedded in a suffix.
+mozilla::Telemetry::ProcessID
+GetProcessFromName(const nsACString& aString)
+{
+  if (StringEndsWith(aString, NS_LITERAL_CSTRING(CONTENT_HISTOGRAM_SUFFIX))) {
+    return ProcessID::Content;
+  }
+  if (StringEndsWith(aString, NS_LITERAL_CSTRING(GPU_HISTOGRAM_SUFFIX))) {
+    return ProcessID::Gpu;
+  }
+  if (StringEndsWith(aString, NS_LITERAL_CSTRING(EXTENSION_HISTOGRAM_SUFFIX))) {
+    return ProcessID::Extension;
+  }
+  return ProcessID::Parent;
+}
+
+const char*
+SuffixForProcessType(mozilla::Telemetry::ProcessID aProcessType)
+{
+  switch (aProcessType) {
+    case ProcessID::Parent:
+      return nullptr;
+    case ProcessID::Content:
+      return CONTENT_HISTOGRAM_SUFFIX;
+    case ProcessID::Gpu:
+      return GPU_HISTOGRAM_SUFFIX;
+    case ProcessID::Extension:
+      return EXTENSION_HISTOGRAM_SUFFIX;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unknown process type");
+      return nullptr;
+  }
+}
+
+CharPtrEntryType*
+internal_GetHistogramMapEntry(const char* aName)
+{
+  nsDependentCString name(aName);
+  ProcessID process = GetProcessFromName(name);
+  const char* suffix = SuffixForProcessType(process);
+  if (!suffix) {
+    return gHistogramMap.GetEntry(aName);
+  }
+
+  auto root = Substring(name, 0, name.Length() - strlen(suffix));
+  return gHistogramMap.GetEntry(PromiseFlatCString(root).get());
+}
+
+nsresult
+internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::HistogramID *id)
+{
+  if (!gInitDone) {
+    return NS_ERROR_FAILURE;
+  }
+
+  CharPtrEntryType *entry = internal_GetHistogramMapEntry(name);
+  if (!entry) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  *id = entry->mData;
+  return NS_OK;
+}
+
+// O(1) histogram lookup by numeric id
+nsresult
+internal_GetHistogramByEnumId(mozilla::Telemetry::HistogramID id, Histogram **ret,
+                              ProcessID aProcessType)
+{
+  static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0};
+  static Histogram* knownContentHistograms[mozilla::Telemetry::HistogramCount] = {0};
+  static Histogram* knownGPUHistograms[mozilla::Telemetry::HistogramCount] = {0};
+  static Histogram* knownExtensionHistograms[mozilla::Telemetry::HistogramCount] = {0};
+
+  Histogram** knownList = nullptr;
+
+  switch (aProcessType) {
+  case ProcessID::Parent:
+    knownList = knownHistograms;
+    break;
+  case ProcessID::Content:
+    knownList = knownContentHistograms;
+    break;
+  case ProcessID::Gpu:
+    knownList = knownGPUHistograms;
+    break;
+  case ProcessID::Extension:
+    knownList = knownExtensionHistograms;
+    break;
+  default:
+    MOZ_ASSERT_UNREACHABLE("unknown process type");
+    return NS_ERROR_FAILURE;
+  }
+
+  Histogram* h = knownList[id];
+  if (h) {
+    *ret = h;
+    return NS_OK;
+  }
+
+  const HistogramInfo &p = gHistograms[id];
+  if (p.keyed) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoCString histogramName;
+  histogramName.Append(p.id());
+  if (const char* suffix = SuffixForProcessType(aProcessType)) {
+    histogramName.AppendASCII(suffix);
+  }
+
+  nsresult rv = internal_HistogramGet(histogramName.get(), p.expiration(),
+                                      p.histogramType, p.min, p.max,
+                                      p.bucketCount, true, &h);
+  if (NS_FAILED(rv))
+    return rv;
+
+#ifdef DEBUG
+  // Check that the C++ Histogram code computes the same ranges as the
+  // Python histogram code.
+  if (!IsExpiredVersion(p.expiration())) {
+    const struct bounds &b = gBucketLowerBoundIndex[id];
+    if (b.length != 0) {
+      MOZ_ASSERT(size_t(b.length) == h->bucket_count(),
+                 "C++/Python bucket # mismatch");
+      for (int i = 0; i < b.length; ++i) {
+        MOZ_ASSERT(gBucketLowerBounds[b.offset + i] == h->ranges(i),
+                   "C++/Python bucket mismatch");
+      }
+    }
+  }
+#endif
+
+  knownList[id] = h;
+  *ret = h;
+  return NS_OK;
+}
+
+nsresult
+internal_GetHistogramByName(const nsACString &name, Histogram **ret)
+{
+  mozilla::Telemetry::HistogramID id;
+  nsresult rv
+    = internal_GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  ProcessID process = GetProcessFromName(name);
+  rv = internal_GetHistogramByEnumId(id, ret, process);
+  if (NS_FAILED(rv))
+    return rv;
+
+  return NS_OK;
+}
+
+
+#if !defined(MOZ_WIDGET_ANDROID)
+
+/**
+ * This clones a histogram |existing| with the id |existingId| to a
+ * new histogram with the name |newName|.
+ * For simplicity this is limited to registered histograms.
+ */
+Histogram*
+internal_CloneHistogram(const nsACString& newName,
+                        mozilla::Telemetry::HistogramID existingId,
+                        Histogram& existing)
+{
+  const HistogramInfo &info = gHistograms[existingId];
+  Histogram *clone = nullptr;
+  nsresult rv;
+
+  rv = internal_HistogramGet(PromiseFlatCString(newName).get(),
+                             info.expiration(),
+                             info.histogramType, existing.declared_min(),
+                             existing.declared_max(), existing.bucket_count(),
+                             true, &clone);
+  if (NS_FAILED(rv)) {
     return nullptr;
   }
 
-  if (isExpired) {
-    gExpiredHistogram = h;
+  Histogram::SampleSet ss;
+  existing.SnapshotSample(&ss);
+  clone->AddSampleSet(ss);
+
+  return clone;
+}
+
+ProcessID
+GetProcessFromName(const std::string& aString)
+{
+  nsDependentCString string(aString.c_str(), aString.length());
+  return GetProcessFromName(string);
+}
+
+Histogram*
+internal_GetSubsessionHistogram(Histogram& existing)
+{
+  mozilla::Telemetry::HistogramID id;
+  nsresult rv
+    = internal_GetHistogramEnumId(existing.histogram_name().c_str(), &id);
+  if (NS_FAILED(rv) || gHistograms[id].keyed) {
+    return nullptr;
   }
 
-  return h;
+  static Histogram* subsession[mozilla::Telemetry::HistogramCount] = {};
+  static Histogram* subsessionContent[mozilla::Telemetry::HistogramCount] = {};
+  static Histogram* subsessionGPU[mozilla::Telemetry::HistogramCount] = {};
+  static Histogram* subsessionExtension[mozilla::Telemetry::HistogramCount] = {};
+
+  Histogram** cache = nullptr;
+
+  ProcessID process = GetProcessFromName(existing.histogram_name());
+  switch (process) {
+  case ProcessID::Parent:
+    cache = subsession;
+    break;
+  case ProcessID::Content:
+    cache = subsessionContent;
+    break;
+  case ProcessID::Gpu:
+    cache = subsessionGPU;
+    break;
+  case ProcessID::Extension:
+    cache = subsessionExtension;
+    break;
+  default:
+    MOZ_ASSERT_UNREACHABLE("unknown process type");
+    return nullptr;
+  }
+
+  if (Histogram* cached = cache[id]) {
+    return cached;
+  }
+
+  NS_NAMED_LITERAL_CSTRING(prefix, SUBSESSION_HISTOGRAM_PREFIX);
+  nsDependentCString existingName(gHistograms[id].id());
+  if (StringBeginsWith(existingName, prefix)) {
+    return nullptr;
+  }
+
+  nsCString subsessionName(prefix);
+  subsessionName.Append(existing.histogram_name().c_str());
+
+  Histogram* clone = internal_CloneHistogram(subsessionName, id, existing);
+  cache[id] = clone;
+  return clone;
 }
+#endif
 
 nsresult
-internal_HistogramAdd(Histogram& histogram,
-                      const HistogramID id,
-                      int32_t value,
-                      ProcessID aProcessType)
+internal_HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset)
 {
   // Check if we are allowed to record the data.
-  bool canRecordDataset = CanRecordDataset(gHistogramInfos[id].dataset,
+  bool canRecordDataset = CanRecordDataset(dataset,
                                            internal_CanRecordBase(),
                                            internal_CanRecordExtended());
-  // If `histogram` is a non-parent-process histogram, then recording-enabled
-  // has been checked in its owner process.
-  if (!canRecordDataset ||
-    (aProcessType == ProcessID::Parent && !internal_IsRecordingEnabled(id))) {
+  if (!canRecordDataset || !histogram.IsRecordingEnabled()) {
     return NS_OK;
   }
 
+#if !defined(MOZ_WIDGET_ANDROID)
+  if (Histogram* subsession = internal_GetSubsessionHistogram(histogram)) {
+    subsession->Add(value);
+  }
+#endif
+
   // It is safe to add to the histogram now: the subsession histogram was already
   // cloned from this so we won't add the sample twice.
   histogram.Add(value);
 
   return NS_OK;
 }
 
+nsresult
+internal_HistogramAdd(Histogram& histogram, int32_t value)
+{
+  uint32_t dataset = nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN;
+  // We only really care about the dataset of the histogram if we are not recording
+  // extended telemetry. Otherwise, we always record histogram data.
+  if (!internal_CanRecordExtended()) {
+    mozilla::Telemetry::HistogramID id;
+    nsresult rv
+      = internal_GetHistogramEnumId(histogram.histogram_name().c_str(), &id);
+    if (NS_FAILED(rv)) {
+      // If we can't look up the dataset, it might be because the histogram was added
+      // at runtime. Since we're not recording extended telemetry, bail out.
+      return NS_OK;
+    }
+    dataset = gHistograms[id].dataset;
+  }
+
+  return internal_HistogramAdd(histogram, value, dataset);
+}
+
+void
+internal_HistogramClear(Histogram& aHistogram, bool onlySubsession)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  if (!XRE_IsParentProcess()) {
+    return;
+  }
+  if (!onlySubsession) {
+    aHistogram.Clear();
+  }
+
+#if !defined(MOZ_WIDGET_ANDROID)
+  if (Histogram* subsession = internal_GetSubsessionHistogram(aHistogram)) {
+    subsession->Clear();
+  }
+#endif
+}
+
 } // namespace
 
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+//
+// PRIVATE: Histogram corruption helpers
+
+namespace {
+
+void internal_Accumulate(mozilla::Telemetry::HistogramID aHistogram, uint32_t aSample);
+
+void
+internal_IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs)
+{
+  for (auto h : hs) {
+    mozilla::Telemetry::HistogramID id;
+    nsresult rv = internal_GetHistogramEnumId(h->histogram_name().c_str(), &id);
+    // This histogram isn't a static histogram, just ignore it.
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    if (gCorruptHistograms[id]) {
+      continue;
+    }
+
+    Histogram::SampleSet ss;
+    h->SnapshotSample(&ss);
+
+    Histogram::Inconsistencies check = h->FindCorruption(ss);
+    bool corrupt = (check != Histogram::NO_INCONSISTENCIES);
+
+    if (corrupt) {
+      mozilla::Telemetry::HistogramID corruptID = mozilla::Telemetry::HistogramCount;
+      if (check & Histogram::RANGE_CHECKSUM_ERROR) {
+        corruptID = mozilla::Telemetry::RANGE_CHECKSUM_ERRORS;
+      } else if (check & Histogram::BUCKET_ORDER_ERROR) {
+        corruptID = mozilla::Telemetry::BUCKET_ORDER_ERRORS;
+      } else if (check & Histogram::COUNT_HIGH_ERROR) {
+        corruptID = mozilla::Telemetry::TOTAL_COUNT_HIGH_ERRORS;
+      } else if (check & Histogram::COUNT_LOW_ERROR) {
+        corruptID = mozilla::Telemetry::TOTAL_COUNT_LOW_ERRORS;
+      }
+      internal_Accumulate(corruptID, 1);
+    }
+
+    gCorruptHistograms[id] = corrupt;
+  }
+}
+
+} // namespace
+
+
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: Histogram reflection helpers
 
 namespace {
 
 bool
@@ -556,16 +774,21 @@ internal_FillRanges(JSContext *cx, JS::H
   return true;
 }
 
 enum reflectStatus
 internal_ReflectHistogramAndSamples(JSContext *cx,
                                     JS::Handle<JSObject*> obj, Histogram *h,
                                     const Histogram::SampleSet &ss)
 {
+  // We don't want to reflect corrupt histograms.
+  if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
+    return REFLECT_CORRUPT;
+  }
+
   if (!(JS_DefineProperty(cx, obj, "min",
                           h->declared_min(), JSPROP_ENUMERATE)
         && JS_DefineProperty(cx, obj, "max",
                              h->declared_max(), JSPROP_ENUMERATE)
         && JS_DefineProperty(cx, obj, "histogram_type",
                              h->histogram_type(), JSPROP_ENUMERATE)
         && JS_DefineProperty(cx, obj, "sum",
                              double(ss.sum()), JSPROP_ENUMERATE))) {
@@ -604,46 +827,110 @@ internal_ReflectHistogramSnapshot(JSCont
                                   JS::Handle<JSObject*> obj, Histogram *h)
 {
   Histogram::SampleSet ss;
   h->SnapshotSample(&ss);
   return internal_ReflectHistogramAndSamples(cx, obj, h, ss);
 }
 
 bool
-internal_ShouldReflectHistogram(Histogram* h, HistogramID id)
+internal_ShouldReflectHistogram(Histogram *h)
 {
-  // Only flag histograms are serialized when they are empty.
-  // This has historical reasons, changing this will require downstream changes.
-  // The cheaper path here is to just deprecate flag histograms in favor
-  // of scalars.
-  uint32_t type = gHistogramInfos[id].histogramType;
-  if (internal_IsEmpty(h) && type != nsITelemetry::HISTOGRAM_FLAG) {
-    return false;
+  const char *name = h->histogram_name().c_str();
+  mozilla::Telemetry::HistogramID id;
+  nsresult rv = internal_GetHistogramEnumId(name, &id);
+  if (NS_FAILED(rv)) {
+    // GetHistogramEnumId generally should not fail.  But a lookup
+    // failure shouldn't prevent us from reflecting histograms into JS.
+    //
+    // However, these two histograms are created by Histogram itself for
+    // tracking corruption.  We have our own histograms for that, so
+    // ignore these two.
+    if (strcmp(name, "Histogram.InconsistentCountHigh") == 0
+        || strcmp(name, "Histogram.InconsistentCountLow") == 0) {
+      return false;
+    }
+    return true;
   }
-
-  return true;
+  return !gCorruptHistograms[id];
 }
 
 } // namespace
 
+
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: class KeyedHistogram
 
 namespace {
 
-KeyedHistogram::KeyedHistogram(HistogramID id, const HistogramInfo& info)
+class KeyedHistogram {
+public:
+  KeyedHistogram(ProcessID processType, const nsACString &name,
+                 const nsACString &expiration,
+                 uint32_t histogramType, uint32_t min, uint32_t max,
+                 uint32_t bucketCount, uint32_t dataset);
+  nsresult GetHistogram(const nsCString& name, Histogram** histogram, bool subsession);
+  Histogram* GetHistogram(const nsCString& name, bool subsession);
+  uint32_t GetHistogramType() const { return mHistogramType; }
+  nsresult GetJSKeys(JSContext* cx, JS::CallArgs& args);
+  nsresult GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
+                         bool subsession, bool clearSubsession);
+
+  void SetRecordingEnabled(bool aEnabled) { mRecordingEnabled = aEnabled; };
+  bool IsRecordingEnabled() const { return mRecordingEnabled; };
+
+  nsresult Add(const nsCString& key, uint32_t aSample);
+  void Clear(bool subsession);
+
+  nsresult GetEnumId(mozilla::Telemetry::HistogramID& id);
+
+private:
+  typedef nsBaseHashtableET<nsCStringHashKey, Histogram*> KeyedHistogramEntry;
+  typedef AutoHashtable<KeyedHistogramEntry> KeyedHistogramMapType;
+  KeyedHistogramMapType mHistogramMap;
+#if !defined(MOZ_WIDGET_ANDROID)
+  KeyedHistogramMapType mSubsessionMap;
+#endif
+
+  static bool ReflectKeyedHistogram(KeyedHistogramEntry* entry,
+                                    JSContext* cx,
+                                    JS::Handle<JSObject*> obj);
+
+  const ProcessID mProcessType;
+  const nsCString mName;
+  const nsCString mExpiration;
+  const uint32_t mHistogramType;
+  const uint32_t mMin;
+  const uint32_t mMax;
+  const uint32_t mBucketCount;
+  const uint32_t mDataset;
+  mozilla::Atomic<bool, mozilla::Relaxed> mRecordingEnabled;
+};
+
+KeyedHistogram::KeyedHistogram(ProcessID processType,
+                               const nsACString &name,
+                               const nsACString &expiration,
+                               uint32_t histogramType,
+                               uint32_t min, uint32_t max,
+                               uint32_t bucketCount, uint32_t dataset)
   : mHistogramMap()
 #if !defined(MOZ_WIDGET_ANDROID)
   , mSubsessionMap()
 #endif
-  , mId(id)
-  , mHistogramInfo(info)
+  , mProcessType(processType)
+  , mName(name)
+  , mExpiration(expiration)
+  , mHistogramType(histogramType)
+  , mMin(min)
+  , mMax(max)
+  , mBucketCount(bucketCount)
+  , mDataset(dataset)
+  , mRecordingEnabled(true)
 {
 }
 
 nsresult
 KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram,
                              bool subsession)
 {
 #if !defined(MOZ_WIDGET_ANDROID)
@@ -652,19 +939,33 @@ KeyedHistogram::GetHistogram(const nsCSt
   KeyedHistogramMapType& map = mHistogramMap;
 #endif
   KeyedHistogramEntry* entry = map.GetEntry(key);
   if (entry) {
     *histogram = entry->mData;
     return NS_OK;
   }
 
-  Histogram* h = internal_CreateHistogramInstance(mHistogramInfo);
-  if (!h) {
-    return NS_ERROR_FAILURE;
+  nsCString histogramName;
+#if !defined(MOZ_WIDGET_ANDROID)
+  if (subsession) {
+    histogramName.AppendLiteral(SUBSESSION_HISTOGRAM_PREFIX);
+  }
+#endif
+  histogramName.Append(mName);
+  histogramName.Append(SuffixForProcessType(mProcessType));
+  histogramName.AppendLiteral(KEYED_HISTOGRAM_NAME_SEPARATOR);
+  histogramName.Append(key);
+
+  Histogram* h;
+  nsresult rv = internal_HistogramGet(histogramName.get(), mExpiration.get(),
+                                      mHistogramType, mMin, mMax, mBucketCount,
+                                      true, &h);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
   *histogram = h;
 
   entry = map.PutEntry(key);
   if (MOZ_UNLIKELY(!entry)) {
     return NS_ERROR_OUT_OF_MEMORY;
@@ -680,26 +981,22 @@ KeyedHistogram::GetHistogram(const nsCSt
   Histogram* h = nullptr;
   if (NS_FAILED(GetHistogram(key, &h, subsession))) {
     return nullptr;
   }
   return h;
 }
 
 nsresult
-KeyedHistogram::Add(const nsCString& key, uint32_t sample,
-                    ProcessID aProcessType)
+KeyedHistogram::Add(const nsCString& key, uint32_t sample)
 {
-  bool canRecordDataset = CanRecordDataset(mHistogramInfo.dataset,
+  bool canRecordDataset = CanRecordDataset(mDataset,
                                            internal_CanRecordBase(),
                                            internal_CanRecordExtended());
-  // If `histogram` is a non-parent-process histogram, then recording-enabled
-  // has been checked in its owner process.
-  if (!canRecordDataset ||
-    (aProcessType == ProcessID::Parent && !internal_IsRecordingEnabled(mId))) {
+  if (!canRecordDataset || !IsRecordingEnabled()) {
     return NS_OK;
   }
 
   Histogram* histogram = GetHistogram(key, false);
   MOZ_ASSERT(histogram);
   if (!histogram) {
     return NS_ERROR_FAILURE;
   }
@@ -807,167 +1104,213 @@ KeyedHistogram::GetJSSnapshot(JSContext*
   if (subsession && clearSubsession) {
     Clear(true);
   }
 #endif
 
   return NS_OK;
 }
 
+nsresult
+KeyedHistogram::GetEnumId(mozilla::Telemetry::HistogramID& id)
+{
+  return internal_GetHistogramEnumId(mName.get(), &id);
+}
+
 } // namespace
 
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+//
+// PRIVATE: KeyedHistogram helpers
+
+namespace {
+
+KeyedHistogram*
+internal_GetKeyedHistogramById(const nsACString &name)
+{
+  if (!gInitDone) {
+    return nullptr;
+  }
+
+  KeyedHistogram* keyed = nullptr;
+  gKeyedHistograms.Get(name, &keyed);
+  return keyed;
+}
+
+} // namespace
+
+
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: thread-unsafe helpers for the external interface
 
 // This is a StaticMutex rather than a plain Mutex (1) so that
 // it gets initialised in a thread-safe manner the first time
 // it is used, and (2) because it is never de-initialised, and
 // a normal Mutex would show up as a leak in BloatView.  StaticMutex
 // also has the "OffTheBooks" property, so it won't show as a leak
 // in BloatView.
 static StaticMutex gTelemetryHistogramMutex;
 
 namespace {
 
+void
+internal_SetHistogramRecordingEnabled(mozilla::Telemetry::HistogramID aID, bool aEnabled)
+{
+  if (gHistograms[aID].keyed) {
+    const nsDependentCString id(gHistograms[aID].id());
+    KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
+    if (keyed) {
+      keyed->SetRecordingEnabled(aEnabled);
+      return;
+    }
+  } else {
+    Histogram *h;
+    nsresult rv = internal_GetHistogramByEnumId(aID, &h, ProcessID::Parent);
+    if (NS_SUCCEEDED(rv)) {
+      h->SetRecordingEnabled(aEnabled);
+      return;
+    }
+  }
+
+  MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
+}
+
 bool
-internal_RemoteAccumulate(HistogramID aId, uint32_t aSample)
+internal_RemoteAccumulate(mozilla::Telemetry::HistogramID aId, uint32_t aSample)
 {
   if (XRE_IsParentProcess()) {
     return false;
   }
-
-  if (!internal_IsRecordingEnabled(aId)) {
+  Histogram *h;
+  nsresult rv = internal_GetHistogramByEnumId(aId, &h, ProcessID::Parent);
+  if (NS_SUCCEEDED(rv) && !h->IsRecordingEnabled()) {
     return true;
   }
-
   TelemetryIPCAccumulator::AccumulateChildHistogram(aId, aSample);
   return true;
 }
 
 bool
-internal_RemoteAccumulate(HistogramID aId,
-                          const nsCString& aKey, uint32_t aSample)
+internal_RemoteAccumulate(mozilla::Telemetry::HistogramID aId,
+                    const nsCString& aKey, uint32_t aSample)
 {
   if (XRE_IsParentProcess()) {
     return false;
   }
-
-  if (!internal_IsRecordingEnabled(aId)) {
-    return true;
+  const HistogramInfo& th = gHistograms[aId];
+  KeyedHistogram* keyed
+     = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
+  MOZ_ASSERT(keyed);
+  if (!keyed->IsRecordingEnabled()) {
+    return false;
   }
-
   TelemetryIPCAccumulator::AccumulateChildKeyedHistogram(aId, aKey, aSample);
   return true;
 }
 
-void internal_Accumulate(HistogramID aId, uint32_t aSample)
+void internal_Accumulate(mozilla::Telemetry::HistogramID aHistogram, uint32_t aSample)
 {
   if (!internal_CanRecordBase() ||
-      internal_RemoteAccumulate(aId, aSample)) {
+      internal_RemoteAccumulate(aHistogram, aSample)) {
+    return;
+  }
+  Histogram *h;
+  nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h, ProcessID::Parent);
+  if (NS_SUCCEEDED(rv)) {
+    internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
+  }
+}
+
+void
+internal_Accumulate(mozilla::Telemetry::HistogramID aID,
+                    const nsCString& aKey, uint32_t aSample)
+{
+  if (!gInitDone || !internal_CanRecordBase() ||
+      internal_RemoteAccumulate(aID, aKey, aSample)) {
+    return;
+  }
+  const HistogramInfo& th = gHistograms[aID];
+  KeyedHistogram* keyed
+     = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
+  MOZ_ASSERT(keyed);
+  keyed->Add(aKey, aSample);
+}
+
+void
+internal_Accumulate(Histogram& aHistogram, uint32_t aSample)
+{
+  if (XRE_IsParentProcess()) {
+    internal_HistogramAdd(aHistogram, aSample);
     return;
   }
 
-  Histogram *h = internal_GetHistogramById(aId, ProcessID::Parent, SessionType::Session);
-  MOZ_ASSERT(h);
-  internal_HistogramAdd(*h, aId, aSample, ProcessID::Parent);
-
-#if !defined(MOZ_WIDGET_ANDROID)
-  h = internal_GetHistogramById(aId, ProcessID::Parent, SessionType::Subsession);
-  MOZ_ASSERT(h);
-  internal_HistogramAdd(*h, aId, aSample, ProcessID::Parent);
-#endif
+  mozilla::Telemetry::HistogramID id;
+  nsresult rv = internal_GetHistogramEnumId(aHistogram.histogram_name().c_str(), &id);
+  if (NS_SUCCEEDED(rv)) {
+    internal_RemoteAccumulate(id, aSample);
+  }
 }
 
 void
-internal_Accumulate(HistogramID aId,
+internal_Accumulate(KeyedHistogram& aKeyed,
                     const nsCString& aKey, uint32_t aSample)
 {
-  if (!gInitDone || !internal_CanRecordBase() ||
-      internal_RemoteAccumulate(aId, aKey, aSample)) {
+  if (XRE_IsParentProcess()) {
+    aKeyed.Add(aKey, aSample);
     return;
   }
 
-  KeyedHistogram* keyed = internal_GetKeyedHistogramById(aId, ProcessID::Parent);
-  MOZ_ASSERT(keyed);
-  keyed->Add(aKey, aSample, ProcessID::Parent);
+  mozilla::Telemetry::HistogramID id;
+  if (NS_SUCCEEDED(aKeyed.GetEnumId(id))) {
+    internal_RemoteAccumulate(id, aKey, aSample);
+  }
 }
 
 void
-internal_AccumulateChild(ProcessID aProcessType, HistogramID aId, uint32_t aSample)
+internal_AccumulateChild(ProcessID aProcessType, mozilla::Telemetry::HistogramID aId, uint32_t aSample)
 {
   if (!internal_CanRecordBase()) {
     return;
   }
-
-  if (Histogram* h = internal_GetHistogramById(aId, aProcessType, SessionType::Session)) {
-    internal_HistogramAdd(*h, aId, aSample, aProcessType);
+  Histogram* h;
+  nsresult rv = internal_GetHistogramByEnumId(aId, &h, aProcessType);
+  if (NS_SUCCEEDED(rv)) {
+    internal_HistogramAdd(*h, aSample, gHistograms[aId].dataset);
   } else {
-    NS_WARNING("Failed GetHistogramById for CHILD");
+    NS_WARNING("NS_FAILED GetHistogramByEnumId for CHILD");
   }
-
-#if !defined(MOZ_WIDGET_ANDROID)
-  if (Histogram* h = internal_GetHistogramById(aId, aProcessType, SessionType::Subsession)) {
-    internal_HistogramAdd(*h, aId, aSample, aProcessType);
-  } else {
-    NS_WARNING("Failed GetHistogramById for CHILD");
-  }
-#endif
 }
 
 void
-internal_AccumulateChildKeyed(ProcessID aProcessType, HistogramID aId,
+internal_AccumulateChildKeyed(ProcessID aProcessType, mozilla::Telemetry::HistogramID aId,
                               const nsCString& aKey, uint32_t aSample)
 {
   if (!gInitDone || !internal_CanRecordBase()) {
     return;
   }
 
-  KeyedHistogram* keyed = internal_GetKeyedHistogramById(aId, aProcessType);
-  MOZ_ASSERT(keyed);
-  keyed->Add(aKey, aSample, aProcessType);
-}
-
-void
-internal_ClearHistogram(HistogramID id, bool onlySubsession)
-{
-  MOZ_ASSERT(XRE_IsParentProcess());
-  if (!XRE_IsParentProcess()) {
+  const char* suffix = SuffixForProcessType(aProcessType);
+  if (!suffix) {
+    MOZ_ASSERT_UNREACHABLE("suffix should not be null");
     return;
   }
 
-  // Handle keyed histograms.
-  if (gHistogramInfos[id].keyed) {
-    for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
-      KeyedHistogram* kh = internal_GetKeyedHistogramById(id, static_cast<ProcessID>(process), /* instantiate = */ false);
-      if (kh) {
-        kh->Clear(onlySubsession);
-      }
-    }
-  }
+  const HistogramInfo& th = gHistograms[aId];
 
-  // Handle plain histograms.
-  // Define the session types we want to clear.
-  nsTArray<SessionType> sessionTypes;
-  if (!onlySubsession) {
-    sessionTypes.AppendElement(SessionType::Session);
-  }
-#if !defined(MOZ_WIDGET_ANDROID)
-  sessionTypes.AppendElement(SessionType::Subsession);
-#endif
+  nsAutoCString id;
+  id.Append(th.id());
+  id.AppendASCII(suffix);
 
-  // Now reset the histograms instances for all processes.
-  for (SessionType sessionType : sessionTypes) {
-    for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
-      internal_ClearHistogramById(id,
-                                  static_cast<ProcessID>(process),
-                                  sessionType);
-    }
-  }
+  KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
+  MOZ_ASSERT(keyed);
+  keyed->Add(aKey, aSample);
 }
 
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
@@ -985,82 +1328,63 @@ internal_ClearHistogram(HistogramID id, 
 // deadlock because the JS_ calls that they make may call back into the
 // TelemetryHistogram interface, hence trying to re-acquire the mutex.
 //
 // This means that these functions potentially race against threads, but
 // that seems preferable to risking deadlock.
 
 namespace {
 
-void internal_JSHistogram_finalize(JSFreeOp*, JSObject*);
-
-static const JSClassOps sJSHistogramClassOps = {
-  nullptr, /* addProperty */
-  nullptr, /* delProperty */
-  nullptr, /* getProperty */
-  nullptr, /* setProperty */
-  nullptr, /* enumerate */
-  nullptr, /* newEnumerate */
-  nullptr, /* resolve */
-  nullptr, /* mayResolve */
-  internal_JSHistogram_finalize
-};
-
 static const JSClass sJSHistogramClass = {
   "JSHistogram",  /* name */
-  JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,  /* flags */
-  &sJSHistogramClassOps
-};
-
-struct JSHistogramData {
-  HistogramID histogramId;
+  JSCLASS_HAS_PRIVATE  /* flags */
 };
 
 bool
 internal_JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   MOZ_ASSERT(obj);
   if (!obj ||
       JS_GetClass(obj) != &sJSHistogramClass) {
-    JS_ReportErrorASCII(cx, "Wrong JS class, expected JSHistogram class");
     return false;
   }
 
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  HistogramID id = data->histogramId;
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
-  uint32_t type = gHistogramInfos[id].histogramType;
+  Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
+  MOZ_ASSERT(h);
+  Histogram::ClassType type = h->histogram_type();
 
   JS::CallArgs args = CallArgsFromVp(argc, vp);
   // This function should always return |undefined| and never fail but
   // rather report failures using the console.
   args.rval().setUndefined();
 
   if (!internal_CanRecordBase()) {
     return true;
   }
 
   uint32_t value = 0;
-  if ((type == nsITelemetry::HISTOGRAM_COUNT) && (args.length() == 0)) {
+  mozilla::Telemetry::HistogramID id;
+  if ((type == base::CountHistogram::COUNT_HISTOGRAM) && (args.length() == 0)) {
     // If we don't have an argument for the count histogram, assume an increment of 1.
     // Otherwise, make sure to run some sanity checks on the argument.
     value = 1;
-  } else if ((args.length() > 0) && args[0].isString() &&
-             gHistogramInfos[id].histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL) {
+  } else if (type == base::LinearHistogram::LINEAR_HISTOGRAM &&
+      (args.length() > 0) && args[0].isString() &&
+      NS_SUCCEEDED(internal_GetHistogramEnumId(h->histogram_name().c_str(), &id)) &&
+      gHistograms[id].histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL) {
     // For categorical histograms we allow passing a string argument that specifies the label.
     nsAutoJSString label;
     if (!label.init(cx, args[0])) {
       LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Invalid string parameter"));
       return true;
     }
 
     // Get label id value.
-    nsresult rv = gHistogramInfos[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
+    nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
     if (NS_FAILED(rv)) {
       LogToBrowserConsole(nsIScriptError::errorFlag,
                           NS_LITERAL_STRING("Unknown label for categorical histogram"));
       return true;
     }
   } else {
     // All other accumulations expect one numerical argument.
     if (!args.length()) {
@@ -1076,140 +1400,108 @@ internal_JSHistogram_Add(JSContext *cx, 
     if (!JS::ToUint32(cx, args[0], &value)) {
       LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to convert argument"));
       return true;
     }
   }
 
   {
     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    internal_Accumulate(id, value);
+    internal_Accumulate(*h, value);
   }
-
   return true;
 }
 
 bool
 internal_JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj ||
       JS_GetClass(obj) != &sJSHistogramClass) {
-    JS_ReportErrorASCII(cx, "Wrong JS class, expected JSHistogram class");
     return false;
   }
 
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  HistogramID id = data->histogramId;
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
-
-  // This is not good standard behavior given that we have histogram instances
-  // covering multiple processes and two session types.
-  // However, changing this requires some broader changes to callers.
-  Histogram* h = internal_GetHistogramById(id, ProcessID::Parent, SessionType::Session);
-  MOZ_ASSERT(h);
-
+  Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx));
-  if (!snapshot) {
+  if (!snapshot)
     return false;
-  }
 
   switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
   case REFLECT_FAILURE:
     return false;
+  case REFLECT_CORRUPT:
+    JS_ReportErrorASCII(cx, "Histogram is corrupt");
+    return false;
   case REFLECT_OK:
     args.rval().setObject(*snapshot);
     return true;
   default:
-    MOZ_ASSERT_UNREACHABLE("Unhandled reflection status.");
+    MOZ_CRASH("unhandled reflection status");
   }
-
-  return true;
 }
 
 bool
 internal_JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj ||
       JS_GetClass(obj) != &sJSHistogramClass) {
-    JS_ReportErrorASCII(cx, "Wrong JS class, expected JSHistogram class");
     return false;
   }
 
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  HistogramID id = data->histogramId;
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
-
   bool onlySubsession = false;
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   // This function should always return |undefined| and never fail but
   // rather report failures using the console.
   args.rval().setUndefined();
 
+
 #if !defined(MOZ_WIDGET_ANDROID)
   if (args.length() >= 1) {
     if (!args[0].isBoolean()) {
       JS_ReportErrorASCII(cx, "Not a boolean");
       return false;
     }
 
     onlySubsession = JS::ToBoolean(args[0]);
   }
 #endif
 
-  internal_ClearHistogram(id, onlySubsession);
+  Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
+  MOZ_ASSERT(h);
+  if (h) {
+    internal_HistogramClear(*h, onlySubsession);
+  }
 
   return true;
 }
 
 // NOTE: Runs without protection from |gTelemetryHistogramMutex|.
 // See comment at the top of this section.
 nsresult
-internal_WrapAndReturnHistogram(HistogramID id, JSContext *cx,
+internal_WrapAndReturnHistogram(Histogram *h, JSContext *cx,
                                 JS::MutableHandle<JS::Value> ret)
 {
   JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &sJSHistogramClass));
-  if (!obj) {
+  if (!obj)
     return NS_ERROR_FAILURE;
-  }
-
   // The 3 functions that are wrapped up here are eventually called
   // by the same thread that runs this function.
   if (!(JS_DefineFunction(cx, obj, "add", internal_JSHistogram_Add, 1, 0)
         && JS_DefineFunction(cx, obj, "snapshot",
                              internal_JSHistogram_Snapshot, 0, 0)
         && JS_DefineFunction(cx, obj, "clear", internal_JSHistogram_Clear, 0, 0))) {
     return NS_ERROR_FAILURE;
   }
-
-  JSHistogramData* data = new JSHistogramData{id};
-  JS_SetPrivate(obj, data);
+  JS_SetPrivate(obj, h);
   ret.setObject(*obj);
-
   return NS_OK;
 }
 
-void
-internal_JSHistogram_finalize(JSFreeOp*, JSObject* obj)
-{
-  if (!obj ||
-      JS_GetClass(obj) != &sJSHistogramClass) {
-    MOZ_ASSERT_UNREACHABLE("Should have the right JS class.");
-    return;
-  }
-
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  delete data;
-}
-
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: JSKeyedHistogram_* functions
 
@@ -1224,66 +1516,38 @@ internal_JSHistogram_finalize(JSFreeOp*,
 //   internal_JSKeyedHistogram_Clear
 //   internal_WrapAndReturnKeyedHistogram
 //
 // Same comments as above, at the JSHistogram_* section, regarding
 // deadlock avoidance, apply.
 
 namespace {
 
-void internal_JSKeyedHistogram_finalize(JSFreeOp*, JSObject*);
-
-static const JSClassOps sJSKeyedHistogramClassOps = {
-  nullptr, /* addProperty */
-  nullptr, /* delProperty */
-  nullptr, /* getProperty */
-  nullptr, /* setProperty */
-  nullptr, /* enumerate */
-  nullptr, /* newEnumerate */
-  nullptr, /* resolve */
-  nullptr, /* mayResolve */
-  internal_JSKeyedHistogram_finalize
-};
-
 static const JSClass sJSKeyedHistogramClass = {
   "JSKeyedHistogram",  /* name */
-  JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,  /* flags */
-  &sJSKeyedHistogramClassOps
+  JSCLASS_HAS_PRIVATE  /* flags */
 };
 
 bool
 internal_KeyedHistogram_SnapshotImpl(JSContext *cx, unsigned argc,
                                      JS::Value *vp,
                                      bool subsession, bool clearSubsession)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj ||
       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
-    JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class");
     return false;
   }
 
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  HistogramID id = data->histogramId;
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
+  KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
+  if (!keyed) {
+    return false;
+  }
 
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  // This function should always return |undefined| and never fail but
-  // rather report failures using the console.
-  args.rval().setUndefined();
-
-  // This is not good standard behavior given that we have histogram instances
-  // covering multiple processes and two session types.
-  // However, changing this requires some broader changes to callers.
-  KeyedHistogram* keyed = internal_GetKeyedHistogramById(id, ProcessID::Parent, /* instantiate = */ true);
-  if (!keyed) {
-    JS_ReportErrorASCII(cx, "Failed to look up keyed histogram");
-    return false;
-  }
 
   if (args.length() == 0) {
     JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
     if (!snapshot) {
       JS_ReportErrorASCII(cx, "Failed to create object");
       return false;
     }
 
@@ -1311,82 +1575,86 @@ internal_KeyedHistogram_SnapshotImpl(JSC
 
   JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
   if (!snapshot) {
     return false;
   }
 
   switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
   case REFLECT_FAILURE:
-    JS_ReportErrorASCII(cx, "Failed to reflect histogram");
+    return false;
+  case REFLECT_CORRUPT:
+    JS_ReportErrorASCII(cx, "Histogram is corrupt");
     return false;
   case REFLECT_OK:
     args.rval().setObject(*snapshot);
     return true;
   default:
     MOZ_CRASH("unhandled reflection status");
   }
-
-  return true;
 }
 
 bool
 internal_JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj ||
       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
-    JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class");
     return false;
   }
 
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  HistogramID id = data->histogramId;
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
+  KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
+  if (!keyed) {
+    return false;
+  }
 
   JS::CallArgs args = CallArgsFromVp(argc, vp);
   // This function should always return |undefined| and never fail but
   // rather report failures using the console.
   args.rval().setUndefined();
   if (args.length() < 1) {
     LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Expected one argument"));
     return true;
   }
 
   nsAutoJSString key;
   if (!args[0].isString() || !key.init(cx, args[0])) {
     LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a string"));
     return true;
   }
 
-  const uint32_t type = gHistogramInfos[id].histogramType;
+  const uint32_t type = keyed->GetHistogramType();
 
   // If we don't have an argument for the count histogram, assume an increment of 1.
   // Otherwise, make sure to run some sanity checks on the argument.
   uint32_t value = 1;
   if ((type != nsITelemetry::HISTOGRAM_COUNT) || (args.length() == 2)) {
     if (args.length() < 2) {
       LogToBrowserConsole(nsIScriptError::errorFlag,
                           NS_LITERAL_STRING("Expected two arguments for this histogram type"));
       return true;
     }
 
     if (type == nsITelemetry::HISTOGRAM_CATEGORICAL && args[1].isString()) {
       // For categorical histograms we allow passing a string argument that specifies the label.
+      mozilla::Telemetry::HistogramID id;
+      if (NS_FAILED(keyed->GetEnumId(id))) {
+        LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to get histogram id."));
+        return true;
+      }
 
       // Get label string.
       nsAutoJSString label;
       if (!label.init(cx, args[1])) {
         LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Invalid string parameter"));
         return true;
       }
 
       // Get label id value.
-      nsresult rv = gHistogramInfos[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
+      nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
       if (NS_FAILED(rv)) {
         LogToBrowserConsole(nsIScriptError::errorFlag,
                             NS_LITERAL_STRING("Unknown label for categorical histogram"));
         return true;
       }
     } else {
       // All other accumulations expect one numerical argument.
       if (!(args[1].isNumber() || args[1].isBoolean())) {
@@ -1396,41 +1664,33 @@ internal_JSKeyedHistogram_Add(JSContext 
 
       if (!JS::ToUint32(cx, args[1], &value)) {
         LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to convert argument"));
         return true;
       }
     }
   }
 
-  internal_Accumulate(id, NS_ConvertUTF16toUTF8(key), value);
-
+  {
+    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+    internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value);
+  }
   return true;
 }
 
 bool
 internal_JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj ||
       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
-    JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class");
     return false;
   }
 
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  HistogramID id = data->histogramId;
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
-
-  // This is not good standard behavior given that we have histogram instances
-  // covering multiple processes and two session types.
-  // However, changing this requires some broader changes to callers.
-  KeyedHistogram* keyed = internal_GetKeyedHistogramById(id, ProcessID::Parent);
-  MOZ_ASSERT(keyed);
+  KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
   if (!keyed) {
     return false;
   }
 
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   return NS_SUCCEEDED(keyed->GetJSKeys(cx, args));
 }
 
@@ -1465,62 +1725,52 @@ internal_JSKeyedHistogram_SnapshotSubses
 #endif
 
 bool
 internal_JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj ||
       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
-    JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class");
     return false;
   }
 
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  HistogramID id = data->histogramId;
-  MOZ_ASSERT(internal_IsHistogramEnumId(id));
+  KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
+  if (!keyed) {
+    return false;
+  }
 
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   // This function should always return |undefined| and never fail but
   // rather report failures using the console.
   args.rval().setUndefined();
 
-  // This is not good standard behavior given that we have histogram instances
-  // covering multiple processes and two session types.
-  // However, changing this requires some broader changes to callers.
-  KeyedHistogram* keyed = internal_GetKeyedHistogramById(id, ProcessID::Parent, /* instantiate = */ false);
-  if (!keyed) {
-    return true;
-  }
-
 #if !defined(MOZ_WIDGET_ANDROID)
   bool onlySubsession = false;
 
   if (args.length() >= 1) {
     if (!(args[0].isNumber() || args[0].isBoolean())) {
       JS_ReportErrorASCII(cx, "Not a boolean");
       return false;
     }
 
     onlySubsession = JS::ToBoolean(args[0]);
   }
 
   keyed->Clear(onlySubsession);
 #else
   keyed->Clear(false);
 #endif
-
   return true;
 }
 
 // NOTE: Runs without protection from |gTelemetryHistogramMutex|.
 // See comment at the top of this section.
 nsresult
-internal_WrapAndReturnKeyedHistogram(HistogramID id, JSContext *cx,
+internal_WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx,
                                      JS::MutableHandle<JS::Value> ret)
 {
   JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &sJSKeyedHistogramClass));
   if (!obj)
     return NS_ERROR_FAILURE;
   // The 6 functions that are wrapped up here are eventually called
   // by the same thread that runs this function.
   if (!(JS_DefineFunction(cx, obj, "add", internal_JSKeyedHistogram_Add, 2, 0)
@@ -1534,125 +1784,148 @@ internal_WrapAndReturnKeyedHistogram(His
 #endif
         && JS_DefineFunction(cx, obj, "keys",
                              internal_JSKeyedHistogram_Keys, 0, 0)
         && JS_DefineFunction(cx, obj, "clear",
                              internal_JSKeyedHistogram_Clear, 0, 0))) {
     return NS_ERROR_FAILURE;
   }
 
-  JSHistogramData* data = new JSHistogramData{id};
-  JS_SetPrivate(obj, data);
+  JS_SetPrivate(obj, h);
   ret.setObject(*obj);
-
   return NS_OK;
 }
 
-void
-internal_JSKeyedHistogram_finalize(JSFreeOp*, JSObject* obj)
-{
-  if (!obj ||
-      JS_GetClass(obj) != &sJSKeyedHistogramClass) {
-    MOZ_ASSERT_UNREACHABLE("Should have the right JS class.");
-    return;
-  }
-
-  JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
-  MOZ_ASSERT(data);
-  delete data;
-}
-
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryHistogram::
 
 // All of these functions are actually in namespace TelemetryHistogram::,
 // but the ::TelemetryHistogram prefix is given explicitly.  This is
 // because it is critical to see which calls from these functions are
 // to another function in this interface.  Mis-identifying "inwards
 // calls" from "calls to another function in this interface" will lead
 // to deadlocking and/or races.  See comments at the top of the file
 // for further (important!) details.
 
+// Create and destroy the singleton StatisticsRecorder object.
+void TelemetryHistogram::CreateStatisticsRecorder()
+{
+  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+  MOZ_ASSERT(!gStatisticsRecorder);
+  gStatisticsRecorder = new base::StatisticsRecorder();
+}
+
+void TelemetryHistogram::DestroyStatisticsRecorder()
+{
+  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+  MOZ_ASSERT(gStatisticsRecorder);
+  if (gStatisticsRecorder) {
+    delete gStatisticsRecorder;
+    gStatisticsRecorder = nullptr;
+  }
+}
+
 void TelemetryHistogram::InitializeGlobalState(bool canRecordBase,
                                                bool canRecordExtended)
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   MOZ_ASSERT(!gInitDone, "TelemetryHistogram::InitializeGlobalState "
              "may only be called once");
 
   gCanRecordBase = canRecordBase;
   gCanRecordExtended = canRecordExtended;
 
-  // gNameToHistogramIDMap should have been pre-sized correctly at the
+  // gHistogramMap should have been pre-sized correctly at the
   // declaration point further up in this file.
 
   // Populate the static histogram name->id cache.
   // Note that the histogram names are statically allocated.
-  for (uint32_t i = 0; i < HistogramCount; i++) {
-    gNameToHistogramIDMap.Put(nsDependentCString(gHistogramInfos[i].name()), HistogramID(i));
+  for (uint32_t i = 0; i < mozilla::Telemetry::HistogramCount; i++) {
+    CharPtrEntryType *entry = gHistogramMap.PutEntry(gHistograms[i].id());
+    entry->mData = (mozilla::Telemetry::HistogramID) i;
   }
 
 #ifdef DEBUG
-  gNameToHistogramIDMap.MarkImmutable();
+  gHistogramMap.MarkImmutable();
 #endif
 
+  mozilla::PodArrayZero(gCorruptHistograms);
+
+  // Create registered keyed histograms
+  for (const auto & h : gHistograms) {
+    if (!h.keyed) {
+      continue;
+    }
+
+    const nsDependentCString id(h.id());
+    const nsDependentCString expiration(h.expiration());
+    gKeyedHistograms.Put(id, new KeyedHistogram(ProcessID::Parent, id, expiration, h.histogramType,
+                                                h.min, h.max, h.bucketCount, h.dataset));
+    if (XRE_IsParentProcess()) {
+      // We must create registered child keyed histograms as well or else the
+      // same code in TelemetrySession.jsm that fails without parent keyed
+      // histograms will fail without child keyed histograms.
+      nsCString contentId(id);
+      contentId.AppendLiteral(CONTENT_HISTOGRAM_SUFFIX);
+      gKeyedHistograms.Put(contentId,
+                           new KeyedHistogram(ProcessID::Content, id, expiration, h.histogramType,
+                                              h.min, h.max, h.bucketCount, h.dataset));
+
+      nsCString gpuId(id);
+      gpuId.AppendLiteral(GPU_HISTOGRAM_SUFFIX);
+      gKeyedHistograms.Put(gpuId,
+                           new KeyedHistogram(ProcessID::Gpu, id, expiration, h.histogramType,
+                                              h.min, h.max, h.bucketCount, h.dataset));
+
+      nsCString extensionId(id);
+      extensionId.AppendLiteral(EXTENSION_HISTOGRAM_SUFFIX);
+      gKeyedHistograms.Put(extensionId,
+                           new KeyedHistogram(ProcessID::Extension, id, expiration, h.histogramType,
+                                              h.min, h.max, h.bucketCount, h.dataset));
+    }
+  }
+
     // Some Telemetry histograms depend on the value of C++ constants and hardcode
     // their values in Histograms.json.
     // We add static asserts here for those values to match so that future changes
     // don't go unnoticed.
     static_assert((JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
-                        gHistogramInfos[mozilla::Telemetry::GC_MINOR_REASON].bucketCount &&
+                        gHistograms[mozilla::Telemetry::GC_MINOR_REASON].bucketCount &&
                   (JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
-                        gHistogramInfos[mozilla::Telemetry::GC_MINOR_REASON_LONG].bucketCount &&
+                        gHistograms[mozilla::Telemetry::GC_MINOR_REASON_LONG].bucketCount &&
                   (JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
-                        gHistogramInfos[mozilla::Telemetry::GC_REASON_2].bucketCount,
+                        gHistograms[mozilla::Telemetry::GC_REASON_2].bucketCount,
                   "NUM_TELEMETRY_REASONS is assumed to be a fixed value in Histograms.json."
                   " If this was an intentional change, update the n_values for the "
                   "following in Histograms.json: GC_MINOR_REASON, GC_MINOR_REASON_LONG, "
                   "GC_REASON_2");
 
     static_assert((mozilla::StartupTimeline::MAX_EVENT_ID + 1) ==
-                        gHistogramInfos[mozilla::Telemetry::STARTUP_MEASUREMENT_ERRORS].bucketCount,
+                        gHistograms[mozilla::Telemetry::STARTUP_MEASUREMENT_ERRORS].bucketCount,
                   "MAX_EVENT_ID is assumed to be a fixed value in Histograms.json.  If this"
                   " was an intentional change, update the n_values for the following in "
                   "Histograms.json: STARTUP_MEASUREMENT_ERRORS");
 
+
   gInitDone = true;
 }
 
 void TelemetryHistogram::DeInitializeGlobalState()
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   gCanRecordBase = false;
   gCanRecordExtended = false;
-  gNameToHistogramIDMap.Clear();
+  gHistogramMap.Clear();
+  gKeyedHistograms.Clear();
   gInitDone = false;
-
-  // FactoryGet `new`s Histograms for us, but requires us to manually delete.
-  for (size_t i = 0; i < HistogramCount; ++i) {
-    for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
-      delete gKeyedHistogramStorage[i][process];
-      gKeyedHistogramStorage[i][process] = nullptr;
-      for (uint32_t session = 0; session <
-        static_cast<uint32_t>(SessionType::Count); ++session) {
-        if (gHistogramStorage[i][process][session] == gExpiredHistogram) {
-          continue;
-        }
-        delete gHistogramStorage[i][process][session];
-        gHistogramStorage[i][process][session] = nullptr;
-      }
-    }
-  }
-  delete gExpiredHistogram;
-  gExpiredHistogram = nullptr;
 }
 
 #ifdef DEBUG
 bool TelemetryHistogram::GlobalStateHasBeenInitialized() {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   return gInitDone;
 }
 #endif
@@ -1682,83 +1955,98 @@ TelemetryHistogram::SetCanRecordExtended
 }
 
 
 void
 TelemetryHistogram::InitHistogramRecordingEnabled()
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   auto processType = XRE_GetProcessType();
-  for (size_t i = 0; i < HistogramCount; ++i) {
-    const HistogramInfo& h = gHistogramInfos[i];
+  for (size_t i = 0; i < mozilla::ArrayLength(gHistograms); ++i) {
+    const HistogramInfo& h = gHistograms[i];
     mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
     internal_SetHistogramRecordingEnabled(id,
                                           CanRecordInProcess(h.record_in_processes,
                                                              processType));
   }
 
   for (auto recordingInitiallyDisabledID : kRecordingInitiallyDisabledIDs) {
     internal_SetHistogramRecordingEnabled(recordingInitiallyDisabledID,
                                           false);
   }
 }
 
 void
-TelemetryHistogram::SetHistogramRecordingEnabled(HistogramID aID,
+TelemetryHistogram::SetHistogramRecordingEnabled(mozilla::Telemetry::HistogramID aID,
                                                  bool aEnabled)
 {
   if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
     return;
   }
 
-  const HistogramInfo& h = gHistogramInfos[aID];
+  const HistogramInfo& h = gHistograms[aID];
   if (!CanRecordInProcess(h.record_in_processes, XRE_GetProcessType())) {
     // Don't permit record_in_process-disabled recording to be re-enabled.
     return;
   }
 
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   internal_SetHistogramRecordingEnabled(aID, aEnabled);
 }
 
 
 nsresult
-TelemetryHistogram::SetHistogramRecordingEnabled(const nsACString& name,
+TelemetryHistogram::SetHistogramRecordingEnabled(const nsACString &id,
                                                  bool aEnabled)
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  HistogramID id;
-  if (NS_FAILED(internal_GetHistogramIdByName(name, &id))) {
-    return NS_ERROR_FAILURE;
+
+  mozilla::Telemetry::HistogramID hId;
+  nsresult rv = internal_GetHistogramEnumId(PromiseFlatCString(id).get(), &hId);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  const HistogramInfo& hi = gHistograms[hId];
+  if (!CanRecordInProcess(hi.record_in_processes, XRE_GetProcessType())) {
+    return NS_OK;
   }
 
-  const HistogramInfo& hi = gHistogramInfos[id];
-  if (CanRecordInProcess(hi.record_in_processes, XRE_GetProcessType())) {
-    internal_SetHistogramRecordingEnabled(id, aEnabled);
+  Histogram *h;
+  rv = internal_GetHistogramByName(id, &h);
+  if (NS_SUCCEEDED(rv)) {
+    h->SetRecordingEnabled(aEnabled);
+    return NS_OK;
   }
-  return NS_OK;
+
+  KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
+  if (keyed) {
+    keyed->SetRecordingEnabled(aEnabled);
+    return NS_OK;
+  }
+
+  return NS_ERROR_FAILURE;
 }
 
 
 void
-TelemetryHistogram::Accumulate(HistogramID aID,
+TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID,
                                uint32_t aSample)
 {
   if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
     return;
   }
 
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   internal_Accumulate(aID, aSample);
 }
 
 void
-TelemetryHistogram::Accumulate(HistogramID aID,
+TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID,
                                const nsCString& aKey, uint32_t aSample)
 {
   if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
     return;
   }
 
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
@@ -1767,54 +2055,54 @@ TelemetryHistogram::Accumulate(Histogram
 
 void
 TelemetryHistogram::Accumulate(const char* name, uint32_t sample)
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   if (!internal_CanRecordBase()) {
     return;
   }
-  HistogramID id;
-  nsresult rv = internal_GetHistogramIdByName(nsDependentCString(name), &id);
+  mozilla::Telemetry::HistogramID id;
+  nsresult rv = internal_GetHistogramEnumId(name, &id);
   if (NS_FAILED(rv)) {
     return;
   }
   internal_Accumulate(id, sample);
 }
 
 void
 TelemetryHistogram::Accumulate(const char* name,
                                const nsCString& key, uint32_t sample)
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   if (!internal_CanRecordBase()) {
     return;
   }
-  HistogramID id;
-  nsresult rv = internal_GetHistogramIdByName(nsDependentCString(name), &id);
+  mozilla::Telemetry::HistogramID id;
+  nsresult rv = internal_GetHistogramEnumId(name, &id);
   if (NS_SUCCEEDED(rv)) {
     internal_Accumulate(id, key, sample);
   }
 }
 
 void
-TelemetryHistogram::AccumulateCategorical(HistogramID aId,
+TelemetryHistogram::AccumulateCategorical(mozilla::Telemetry::HistogramID aId,
                                           const nsCString& label)
 {
   if (NS_WARN_IF(!internal_IsHistogramEnumId(aId))) {
     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
     return;
   }
 
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   if (!internal_CanRecordBase()) {
     return;
   }
   uint32_t labelId = 0;
-  if (NS_FAILED(gHistogramInfos[aId].label_id(label.get(), &labelId))) {
+  if (NS_FAILED(gHistograms[aId].label_id(label.get(), &labelId))) {
     return;
   }
   internal_Accumulate(aId, labelId);
 }
 
 void
 TelemetryHistogram::AccumulateChild(ProcessID aProcessType,
                                     const nsTArray<Accumulation>& aAccumulations)
@@ -1854,139 +2142,164 @@ TelemetryHistogram::AccumulateChildKeyed
                                   aAccumulations[i].mSample);
   }
 }
 
 nsresult
 TelemetryHistogram::GetHistogramById(const nsACString &name, JSContext *cx,
                                      JS::MutableHandle<JS::Value> ret)
 {
-  HistogramID id;
+  Histogram *h = nullptr;
   {
     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    nsresult rv = internal_GetHistogramIdByName(name, &id);
-    if (NS_FAILED(rv)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (gHistogramInfos[id].keyed) {
-      return NS_ERROR_FAILURE;
-    }
+    nsresult rv = internal_GetHistogramByName(name, &h);
+    if (NS_FAILED(rv))
+      return rv;
   }
   // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnHistogram(id, cx, ret);
+  return internal_WrapAndReturnHistogram(h, cx, ret);
 }
 
 nsresult
 TelemetryHistogram::GetKeyedHistogramById(const nsACString &name,
                                           JSContext *cx,
                                           JS::MutableHandle<JS::Value> ret)
 {
-  HistogramID id;
+  KeyedHistogram* keyed = nullptr;
   {
     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    nsresult rv = internal_GetHistogramIdByName(name, &id);
-    if (NS_FAILED(rv)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (!gHistogramInfos[id].keyed) {
+    if (!gKeyedHistograms.Get(name, &keyed)) {
       return NS_ERROR_FAILURE;
     }
   }
   // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnKeyedHistogram(id, cx, ret);
+  return internal_WrapAndReturnKeyedHistogram(keyed, cx, ret);
 }
 
 const char*
-TelemetryHistogram::GetHistogramName(HistogramID id)
+TelemetryHistogram::GetHistogramName(mozilla::Telemetry::HistogramID id)
 {
   if (NS_WARN_IF(!internal_IsHistogramEnumId(id))) {
     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
     return nullptr;
   }
 
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  const HistogramInfo& h = gHistogramInfos[id];
-  return h.name();
+  const HistogramInfo& h = gHistograms[id];
+  return h.id();
 }
 
 nsresult
 TelemetryHistogram::CreateHistogramSnapshots(JSContext *cx,
                                              JS::MutableHandle<JS::Value> ret,
                                              bool subsession,
                                              bool clearSubsession)
 {
   // Runs without protection from |gTelemetryHistogramMutex|
   JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
-  if (!root_obj) {
+  if (!root_obj)
     return NS_ERROR_FAILURE;
-  }
   ret.setObject(*root_obj);
 
   // Include the GPU process in histogram snapshots only if we actually tried
   // to launch a process for it.
   bool includeGPUProcess = false;
   if (auto gpm = mozilla::gfx::GPUProcessManager::Get()) {
     includeGPUProcess = gpm->AttemptedGPUProcess();
   }
 
-#if !defined(MOZ_WIDGET_ANDROID)
-  SessionType sessionType = SessionType(subsession);
-#else
-  SessionType sessionType = SessionType::Session;
-#endif
+  // Ensure that all the HISTOGRAM_FLAG & HISTOGRAM_COUNT histograms have
+  // been created, so that their values are snapshotted.
+  auto processType = XRE_GetProcessType();
+  for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
+    const HistogramInfo& hi = gHistograms[i];
+    if (hi.keyed || !CanRecordInProcess(hi.record_in_processes, processType)) {
+      continue;
+    }
+    const uint32_t type = hi.histogramType;
+    if (type == nsITelemetry::HISTOGRAM_FLAG ||
+        type == nsITelemetry::HISTOGRAM_COUNT) {
+      Histogram *h;
+      mozilla::DebugOnly<nsresult> rv;
+      mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
+
+      for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
+        if ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess) {
+          continue;
+        }
+        rv = internal_GetHistogramByEnumId(id, &h, ProcessID(process));
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+      }
+    }
+  }
+
+  StatisticsRecorder::Histograms hs;
+  StatisticsRecorder::GetHistograms(&hs);
 
-  for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
-    JS::Rooted<JSObject*> processObject(cx, JS_NewPlainObject(cx));
-    if (!processObject) {
-      return NS_ERROR_FAILURE;
+  // We identify corrupt histograms first, rather than interspersing it
+  // in the loop below, to ensure that our corruption statistics don't
+  // depend on histogram enumeration order.
+  //
+  // Of course, we hope that all of these corruption-statistics
+  // histograms are not themselves corrupt...
+  internal_IdentifyCorruptHistograms(hs);
+
+  // OK, now we can actually reflect things.
+  JS::Rooted<JSObject*> hobj(cx);
+  for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
+    const HistogramInfo& hi = gHistograms[i];
+    if (hi.keyed) {
+      continue;
     }
-    if (!JS_DefineProperty(cx, root_obj, GetNameForProcessID(ProcessID(process)),
-                           processObject, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-    for (size_t i = 0; i < HistogramCount; ++i) {
-      const HistogramInfo& info = gHistogramInfos[i];
-      if (info.keyed) {
+
+    Histogram* h = nullptr;
+    mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
+
+    for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
+      if (!CanRecordInProcess(hi.record_in_processes, ProcessID(process)) ||
+        ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess)) {
+        continue;
+      }
+      nsresult rv = internal_GetHistogramByEnumId(id, &h, ProcessID(process));
+      if (NS_WARN_IF(NS_FAILED(rv)) || !internal_ShouldReflectHistogram(h) ||
+        internal_IsEmpty(h) || internal_IsExpired(h)) {
         continue;
       }
 
-      HistogramID id = HistogramID(i);
-
-      if (!CanRecordInProcess(info.record_in_processes, ProcessID(process)) ||
-        ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess)) {
-        continue;
+      Histogram* original = h;
+#if !defined(MOZ_WIDGET_ANDROID)
+      if (subsession) {
+        h = internal_GetSubsessionHistogram(*h);
+        if (!h) {
+          continue;
+        }
       }
+#endif
 
-      bool shouldInstantiate =
-        info.histogramType == nsITelemetry::HISTOGRAM_FLAG;
-      Histogram* h = internal_GetHistogramById(id, ProcessID(process),
-                                               sessionType,
-                                               shouldInstantiate);
-      if (!h || internal_IsExpired(h) || !internal_ShouldReflectHistogram(h, id)) {
-        continue;
-      }
-
-      JS::Rooted<JSObject*> hobj(cx, JS_NewPlainObject(cx));
+      hobj = JS_NewPlainObject(cx);
       if (!hobj) {
         return NS_ERROR_FAILURE;
       }
       switch (internal_ReflectHistogramSnapshot(cx, hobj, h)) {
+      case REFLECT_CORRUPT:
+        // We can still hit this case even if ShouldReflectHistograms
+        // returns true.  The histogram lies outside of our control
+        // somehow; just skip it.
+        continue;
       case REFLECT_FAILURE:
         return NS_ERROR_FAILURE;
       case REFLECT_OK:
-        if (!JS_DefineProperty(cx, processObject, gHistogramInfos[id].name(),
+        if (!JS_DefineProperty(cx, root_obj, original->histogram_name().c_str(),
                                hobj, JSPROP_ENUMERATE)) {
           return NS_ERROR_FAILURE;
         }
       }
 
 #if !defined(MOZ_WIDGET_ANDROID)
-      if ((sessionType == SessionType::Subsession) && clearSubsession) {
+      if (subsession && clearSubsession) {
         h->Clear();
       }
 #endif
     }
   }
   return NS_OK;
 }
 
@@ -2006,89 +2319,57 @@ TelemetryHistogram::RegisteredKeyedHisto
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   return internal_GetRegisteredHistogramIds(true,
                                             aDataset, aCount, aHistograms);
 }
 
 nsresult
 TelemetryHistogram::GetKeyedHistogramSnapshots(JSContext *cx,
-                                               JS::MutableHandle<JS::Value> ret,
-                                               bool subsession,
-                                               bool clearSubsession)
+                                               JS::MutableHandle<JS::Value> ret)
 {
   // Runs without protection from |gTelemetryHistogramMutex|
   JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
   if (!obj) {
     return NS_ERROR_FAILURE;
   }
-  ret.setObject(*obj);
 
-  // Include the GPU process in histogram snapshots only if we actually tried
-  // to launch a process for it.
-  bool includeGPUProcess = false;
-  if (auto gpm = mozilla::gfx::GPUProcessManager::Get()) {
-    includeGPUProcess = gpm->AttemptedGPUProcess();
-  }
-
-  for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
-    JS::Rooted<JSObject*> processObject(cx, JS_NewPlainObject(cx));
-    if (!processObject) {
-      return NS_ERROR_FAILURE;
-    }
-    if (!JS_DefineProperty(cx, obj, GetNameForProcessID(ProcessID(process)),
-                           processObject, JSPROP_ENUMERATE)) {
+  for (auto iter = gKeyedHistograms.Iter(); !iter.Done(); iter.Next()) {
+    JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
+    if (!snapshot) {
       return NS_ERROR_FAILURE;
     }
-    for (size_t id = 0; id < HistogramCount; ++id) {
-      const HistogramInfo& info = gHistogramInfos[id];
-      if (!info.keyed) {
-        continue;
-      }
-
-      if (!CanRecordInProcess(info.record_in_processes, ProcessID(process)) ||
-        ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess)) {
-        continue;
-      }
 
-      KeyedHistogram* keyed = internal_GetKeyedHistogramById(HistogramID(id),
-                                                             ProcessID(process),
-                                                             /* instantiate = */ false);
-      if (!keyed) {
-        continue;
-      }
+    if (!NS_SUCCEEDED(iter.Data()->GetJSSnapshot(cx, snapshot, false, false))) {
+      return NS_ERROR_FAILURE;
+    }
 
-      JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
-      if (!snapshot) {
-        return NS_ERROR_FAILURE;
-      }
-
-      if (!NS_SUCCEEDED(keyed->GetJSSnapshot(cx, snapshot, subsession,
-                                             clearSubsession))) {
-        return NS_ERROR_FAILURE;
-      }
-
-      if (!JS_DefineProperty(cx, processObject, gHistogramInfos[id].name(),
-                             snapshot, JSPROP_ENUMERATE)) {
-        return NS_ERROR_FAILURE;
-      }
+    if (!JS_DefineProperty(cx, obj, PromiseFlatCString(iter.Key()).get(),
+                           snapshot, JSPROP_ENUMERATE)) {
+      return NS_ERROR_FAILURE;
     }
   }
+
+  ret.setObject(*obj);
   return NS_OK;
 }
 
 size_t
 TelemetryHistogram::GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf
                                                       aMallocSizeOf)
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  // TODO
-  return 0;
+  return gHistogramMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
 }
 
 size_t
 TelemetryHistogram::GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf
                                                      aMallocSizeOf)
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  // TODO
-  return 0;
+  StatisticsRecorder::Histograms hs;
+  StatisticsRecorder::GetHistograms(&hs);
+  size_t n = 0;
+  for (auto h : hs) {
+    n += h->SizeOfIncludingThis(aMallocSizeOf);
+  }
+  return n;
 }
--- a/toolkit/components/telemetry/TelemetryHistogram.h
+++ b/toolkit/components/telemetry/TelemetryHistogram.h
@@ -14,16 +14,19 @@
 
 // This module is internal to Telemetry.  It encapsulates Telemetry's
 // histogram accumulation and storage logic.  It should only be used by
 // Telemetry.cpp.  These functions should not be used anywhere else.
 // For the public interface to Telemetry functionality, see Telemetry.h.
 
 namespace TelemetryHistogram {
 
+void CreateStatisticsRecorder();
+void DestroyStatisticsRecorder();
+
 void InitializeGlobalState(bool canRecordBase, bool canRecordExtended);
 void DeInitializeGlobalState();
 #ifdef DEBUG
 bool GlobalStateHasBeenInitialized();
 #endif
 
 bool CanRecordBase();
 void SetCanRecordBase(bool b);
@@ -67,18 +70,17 @@ nsresult
 RegisteredHistograms(uint32_t aDataset, uint32_t *aCount,
                      char*** aHistograms);
 
 nsresult
 RegisteredKeyedHistograms(uint32_t aDataset, uint32_t *aCount,
                           char*** aHistograms);
 
 nsresult
-GetKeyedHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret,
-                           bool subsession, bool clearSubsession);
+GetKeyedHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret);
 
 size_t
 GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
 size_t
 GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
 } // namespace TelemetryHistogram
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -35,16 +35,23 @@ const REASON_ABORTED_SESSION = "aborted-
 const REASON_DAILY = "daily";
 const REASON_SAVED_SESSION = "saved-session";
 const REASON_GATHER_PAYLOAD = "gather-payload";
 const REASON_GATHER_SUBSESSION_PAYLOAD = "gather-subsession-payload";
 const REASON_TEST_PING = "test-ping";
 const REASON_ENVIRONMENT_CHANGE = "environment-change";
 const REASON_SHUTDOWN = "shutdown";
 
+const HISTOGRAM_SUFFIXES = {
+  PARENT: "",
+  CONTENT: "#content",
+  GPU: "#gpu",
+  EXTENSION: "#extension",
+};
+
 const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange";
 
 const MS_IN_ONE_HOUR  = 60 * 60 * 1000;
 const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessionLength", 5 * 60) * 1000;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
 
@@ -924,53 +931,62 @@ var Impl = {
       registered = registered.filter(n => !n.startsWith("TELEMETRY_TEST_"));
     }
     registered = registered.concat(registered.map(n => "STARTUP_" + n));
 
     let hls = subsession ? Telemetry.snapshotSubsessionHistograms(clearSubsession)
                          : Telemetry.histogramSnapshots;
     let ret = {};
 
-    for (let [process, histograms] of Object.entries(hls)) {
-      ret[process] = {};
-      for (let [name, value] of Object.entries(histograms)) {
-        if (registered.includes(name)) {
-          ret[process][name] = this.packHistogram(value);
+    for (let name of registered) {
+      for (let suffix of Object.values(HISTOGRAM_SUFFIXES)) {
+        if (name + suffix in hls) {
+          if (!(suffix in ret)) {
+            ret[suffix] = {};
+          }
+          ret[suffix][name] = this.packHistogram(hls[name + suffix]);
         }
       }
     }
 
     return ret;
   },
 
   getKeyedHistograms(subsession, clearSubsession) {
     let registered =
       Telemetry.registeredKeyedHistograms(this.getDatasetType(), []);
     if (this._testing == false) {
       // Omit telemetry test histograms outside of tests.
       registered = registered.filter(id => !id.startsWith("TELEMETRY_TEST_"));
     }
-
-    let khs = subsession ? Telemetry.snapshotSubsessionKeyedHistograms(clearSubsession)
-                         : Telemetry.keyedHistogramSnapshots;
     let ret = {};
 
-    for (let [process, histograms] of Object.entries(khs)) {
-      ret[process] = {};
-      for (let [name, value] of Object.entries(histograms)) {
-        if (registered.includes(name)) {
-          let keys = Object.keys(value);
-          if (keys.length == 0) {
-            // Skip empty keyed histogram
-            continue;
-          }
-          ret[process][name] = {};
-          for (let [key, hgram] of Object.entries(value)) {
-            ret[process][name][key] = this.packHistogram(hgram);
-          }
+    for (let id of registered) {
+      for (let suffix of Object.values(HISTOGRAM_SUFFIXES)) {
+        let keyed = Telemetry.getKeyedHistogramById(id + suffix);
+        let snapshot = null;
+        if (subsession) {
+          snapshot = clearSubsession ? keyed.snapshotSubsessionAndClear()
+                                     : keyed.subsessionSnapshot();
+        } else {
+          snapshot = keyed.snapshot();
+        }
+
+        let keys = Object.keys(snapshot);
+        if (keys.length == 0) {
+          // Skip empty keyed histogram.
+          continue;
+        }
+
+        if (!(suffix in ret)) {
+          ret[suffix] = {};
+        }
+        ret[suffix][id] = {};
+        for (let key of keys) {
+          ret[suffix][id][key] = this.packHistogram(snapshot[key]);
         }
       }
     }
 
     return ret;
   },
 
   /**
@@ -1305,50 +1321,50 @@ var Impl = {
 
     // 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), {});
     let events = protect(() => this.getEvents(isSubsession, clearSubsession))
 
-    payloadObj.histograms = histograms.parent || {};
-    payloadObj.keyedHistograms = keyedHistograms.parent || {};
+    payloadObj.histograms = histograms[HISTOGRAM_SUFFIXES.PARENT] || {};
+    payloadObj.keyedHistograms = keyedHistograms[HISTOGRAM_SUFFIXES.PARENT] || {};
     payloadObj.processes = {
       parent: {
         scalars: scalars["parent"] || {},
         keyedScalars: keyedScalars["parent"] || {},
         events: events["parent"] || [],
       },
       content: {
         scalars: scalars["content"],
         keyedScalars: keyedScalars["content"],
-        histograms: histograms["content"],
-        keyedHistograms: keyedHistograms["content"],
+        histograms: histograms[HISTOGRAM_SUFFIXES.CONTENT],
+        keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.CONTENT],
         events: events["content"] || [],
       },
       extension: {
         scalars: scalars["extension"],
         keyedScalars: keyedScalars["extension"],
-        histograms: histograms["extension"],
-        keyedHistograms: keyedHistograms["extension"],
+        histograms: histograms[HISTOGRAM_SUFFIXES.EXTENSION],
+        keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.EXTENSION],
         events: events["extension"] || [],
       },
     };
 
     // Only include the GPU process if we've accumulated data for it.
-    if ("gpu" in histograms ||
-        "gpu" in keyedHistograms ||
+    if (HISTOGRAM_SUFFIXES.GPU in histograms ||
+        HISTOGRAM_SUFFIXES.GPU in keyedHistograms ||
         "gpu" in scalars ||
         "gpu" in keyedScalars) {
       payloadObj.processes.gpu = {
         scalars: scalars["gpu"],
         keyedScalars: keyedScalars["gpu"],
-        histograms: histograms["gpu"],
-        keyedHistograms: keyedHistograms["gpu"],
+        histograms: histograms[HISTOGRAM_SUFFIXES.GPU],
+        keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.GPU],
         events: events["gpu"] || [],
       };
     }
 
     payloadObj.info = info;
 
     // Add extended set measurements for chrome process.
     if (Telemetry.canRecordExtended) {
--- a/toolkit/components/telemetry/gen-histogram-data.py
+++ b/toolkit/components/telemetry/gen-histogram-data.py
@@ -36,17 +36,17 @@ def print_array_entry(output, histogram,
         print("#endif", file=output)
 
 
 def write_histogram_table(output, histograms):
     string_table = StringTable()
     label_table = []
     label_count = 0
 
-    print("constexpr HistogramInfo gHistogramInfos[] = {", file=output)
+    print("constexpr HistogramInfo gHistograms[] = {", file=output)
     for histogram in histograms:
         name_index = string_table.stringIndex(histogram.name())
         exp_index = string_table.stringIndex(histogram.expiration())
 
         labels = histogram.labels()
         label_index = 0
         if len(labels) > 0:
             label_index = label_count
--- a/toolkit/components/telemetry/nsITelemetry.idl
+++ b/toolkit/components/telemetry/nsITelemetry.idl
@@ -47,18 +47,18 @@ interface nsITelemetry : nsISupports
    * DATASET_RELEASE_CHANNEL_OPTIN - the extended dataset that is opt-in on release,
    *                                 opt-out on pre-release channels.
    */
   const unsigned long DATASET_RELEASE_CHANNEL_OPTOUT = 0;
   const unsigned long DATASET_RELEASE_CHANNEL_OPTIN = 1;
 
 
   /**
-   * An object containing a snapshot from all of the currently registered histograms from all processes.
-   * { process1: {name1: {data1}, name2:{data2}...} }
+   * An object containing a snapshot from all of the currently registered histograms.
+   * { name1: {data1}, name2:{data2}...}
    * where data is consists of the following properties:
    *   min - Minimal bucket size
    *   max - Maximum bucket size
    *   histogram_type - HISTOGRAM_EXPONENTIAL, HISTOGRAM_LINEAR, HISTOGRAM_BOOLEAN
    *                    or HISTOGRAM_COUNT
    *   counts - array representing contents of the buckets in the histogram
    *   ranges -  an array with calculated bucket sizes
    *   sum - sum of the bucket contents
@@ -252,31 +252,23 @@ interface nsITelemetry : nsISupports
    *   dataset() - identifies what dataset this is in: DATASET_RELEASE_CHANNEL_OPTOUT or ...OPTIN.
    *               This is intended to be only used in tests.
    */
   [implicit_jscontext]
   jsval getHistogramById(in ACString id);
 
   /*
    * An object containing a snapshot from all of the currently registered keyed histograms.
-   * { process1: {name1: {histogramData1}, name2:{histogramData2}...}}
+   * { name1: {histogramData1}, name2:{histogramData2}...}
    * where the histogramData is as described in histogramSnapshots.
    */
   [implicit_jscontext]
   readonly attribute jsval keyedHistogramSnapshots;
 
   /**
-   * Get a snapshot of the internally duplicated subsession keyed histograms.
-   * @param clear Whether to clear out the subsession histograms after snapshotting.
-   * @return An object as histogramSnapshots, except this contains the internally duplicated keyed histograms for subsession telemetry.
-   */
-  [implicit_jscontext]
-  jsval snapshotSubsessionKeyedHistograms([optional] in boolean clear);
-
-  /**
    * Returns an array whose values are the names of histograms defined
    * in Histograms.json.
    *
    * @param dataset - DATASET_RELEASE_CHANNEL_OPTOUT or ...OPTIN
    */
   void registeredKeyedHistograms(in uint32_t dataset, out uint32_t count,
                                  [retval, array, size_is(count)] out string histograms);
 
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
@@ -67,17 +67,17 @@ function check_histogram(histogram_type,
   var s = h.snapshot();
   // verify properties
   do_check_eq(sum, s.sum);
 
   // there should be exactly one element per bucket
   for (let i of s.counts) {
     do_check_eq(i, 1);
   }
-  var hgrams = Telemetry.histogramSnapshots.parent;
+  var hgrams = Telemetry.histogramSnapshots
   let gh = hgrams[name]
   do_check_eq(gh.histogram_type, histogram_type);
 
   do_check_eq(gh.min, min)
   do_check_eq(gh.max, max)
 
   // Check that booleans work with nonboolean histograms
   h.add(false);
@@ -109,18 +109,17 @@ function test_instantiate() {
   const ID = "TELEMETRY_TEST_COUNT";
   let h = Telemetry.getHistogramById(ID);
 
   // Instantiate the subsession histogram through |add| and make sure they match.
   // This MUST be the first use of "TELEMETRY_TEST_COUNT" in this file, otherwise
   // |add| will not instantiate the histogram.
   h.add(1);
   let snapshot = h.snapshot();
-  let subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.ok(ID in subsession);
+  let subsession = Telemetry.snapshotSubsessionHistograms();
   Assert.equal(snapshot.sum, subsession[ID].sum,
                "Histogram and subsession histogram sum must match.");
   // Clear the histogram, so we don't void the assumptions from the other tests.
   h.clear();
 });
 
 add_task(async function test_parameterChecks() {
   let kinds = [Telemetry.HISTOGRAM_EXPONENTIAL, Telemetry.HISTOGRAM_LINEAR]
@@ -167,17 +166,17 @@ add_task(async function test_parameterCo
     h.clear();
   }
 });
 
 add_task(async function test_noSerialization() {
   // Instantiate the storage for this histogram and make sure it doesn't
   // get reflected into JS, as it has no interesting data in it.
   Telemetry.getHistogramById("NEWTAB_PAGE_PINNED_SITES_COUNT");
-  do_check_false("NEWTAB_PAGE_PINNED_SITES_COUNT" in Telemetry.histogramSnapshots.parent);
+  do_check_false("NEWTAB_PAGE_PINNED_SITES_COUNT" in Telemetry.histogramSnapshots);
 });
 
 add_task(async function test_boolean_histogram() {
   var h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN");
   var r = h.snapshot().ranges;
   // boolean histograms ignore numeric parameters
   do_check_eq(uneval(r), uneval([0, 1, 2]))
   for (var i = 0;i < r.length;i++) {
@@ -330,17 +329,17 @@ add_task(async function test_API_return_
     hist.clear(),
     hist.add(1),
     keyedHist.clear(),
     keyedHist.add("some-key", 1),
   ];
 
   for (let returnValue of RETURN_VALUES) {
     Assert.strictEqual(returnValue, undefined,
-                       "The function must return undefined");
+                       "The function must return undefined.");
   }
 });
 
 add_task(async function test_getHistogramById() {
   try {
     Telemetry.getHistogramById("nonexistent");
     do_throw("This can't happen");
   } catch (e) {
@@ -434,24 +433,18 @@ add_task(async function test_histogramRe
 add_task(async function test_expired_histogram() {
   var test_expired_id = "TELEMETRY_TEST_EXPIRED";
   var dummy = Telemetry.getHistogramById(test_expired_id);
   var rh = Telemetry.registeredHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, []);
   Assert.ok(!!rh);
 
   dummy.add(1);
 
-  for (let process of ["main", "content", "gpu", "extension"]) {
-    if (!(process in Telemetry.histogramSnapshots)) {
-      do_print("Nothing present for process " + process);
-      continue;
-    }
-    do_check_eq(Telemetry.histogramSnapshots[process]["__expired__"], undefined);
-  }
-  do_check_eq(Telemetry.histogramSnapshots.parent[test_expired_id], undefined);
+  do_check_eq(Telemetry.histogramSnapshots["__expired__"], undefined);
+  do_check_eq(Telemetry.histogramSnapshots[test_expired_id], undefined);
   do_check_eq(rh[test_expired_id], undefined);
 });
 
 add_task(async function test_keyed_histogram() {
   // Check that invalid names get rejected.
 
   let threw = false;
   try {
@@ -498,17 +491,17 @@ add_task(async function test_keyed_boole
   h.add(key, false);
   testKeys.push(key);
   testSnapShot[key] = testHistograms[2];
   testSnapShot[key].sum = 0;
   testSnapShot[key].counts = [1, 0, 0];
   Assert.deepEqual(h.keys().sort(), testKeys);
   Assert.deepEqual(h.snapshot(), testSnapShot);
 
-  let allSnapshots = Telemetry.keyedHistogramSnapshots.parent;
+  let allSnapshots = Telemetry.keyedHistogramSnapshots;
   Assert.deepEqual(allSnapshots[KEYED_ID], testSnapShot);
 
   h.clear();
   Assert.deepEqual(h.keys(), []);
   Assert.deepEqual(h.snapshot(), {});
 });
 
 add_task(async function test_keyed_count_histogram() {
@@ -554,17 +547,17 @@ add_task(async function test_keyed_count
   testKeys.push(key);
   testHistograms[4].counts[0] = 1;
   testHistograms[4].sum = 1;
   testSnapShot[key] = testHistograms[4];
 
   Assert.deepEqual(h.keys().sort(), testKeys);
   Assert.deepEqual(h.snapshot(), testSnapShot);
 
-  let allSnapshots = Telemetry.keyedHistogramSnapshots.parent;
+  let allSnapshots = Telemetry.keyedHistogramSnapshots;
   Assert.deepEqual(allSnapshots[KEYED_ID], testSnapShot);
 
   // Test clearing categorical histogram.
   h.clear();
   Assert.deepEqual(h.keys(), []);
   Assert.deepEqual(h.snapshot(), {});
 
   // Test leaving out the value argument. That should increment by 1.
@@ -627,17 +620,17 @@ add_task(async function test_keyed_flag_
     "sum": 1,
     "ranges": [0, 1, 2],
     "counts": [0, 1, 0]
   };
 
   Assert.deepEqual(h.keys().sort(), [KEY]);
   Assert.deepEqual(h.snapshot(), testSnapshot);
 
-  let allSnapshots = Telemetry.keyedHistogramSnapshots.parent;
+  let allSnapshots = Telemetry.keyedHistogramSnapshots;
   Assert.deepEqual(allSnapshots[KEYED_ID], testSnapshot);
 
   h.clear();
   Assert.deepEqual(h.keys(), []);
   Assert.deepEqual(h.snapshot(), {});
 });
 
 add_task(async function test_keyed_histogram_recording() {
@@ -777,17 +770,17 @@ add_task(async function test_keyed_histo
     "Keyed histogram add should not record when recording is disabled");
 });
 
 add_task(async function test_histogramSnapshots() {
   let keyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT");
   keyed.add("a", 1);
 
   // Check that keyed histograms are not returned
-  Assert.ok(!("TELEMETRY_TEST_KEYED_COUNT" in Telemetry.histogramSnapshots.parent));
+  Assert.ok(!("TELEMETRY_TEST_KEYED_COUNT#a" in Telemetry.histogramSnapshots));
 });
 
 add_task(async function test_datasets() {
   // Check that datasets work as expected.
 
   const RELEASE_CHANNEL_OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
   const RELEASE_CHANNEL_OPTIN  = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN;
 
@@ -813,94 +806,94 @@ add_task(async function test_datasets() 
   Assert.ok(!registered.has("TELEMETRY_TEST_KEYED_FLAG"));
   Assert.ok(registered.has("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"));
 });
 
 add_task({
   skip_if: () => gIsAndroid
 },
 function test_subsession() {
-  const COUNT = "TELEMETRY_TEST_COUNT";
+  const ID = "TELEMETRY_TEST_COUNT";
   const FLAG = "TELEMETRY_TEST_FLAG";
-  let h = Telemetry.getHistogramById(COUNT);
+  let h = Telemetry.getHistogramById(ID);
   let flag = Telemetry.getHistogramById(FLAG);
 
   // Both original and duplicate should start out the same.
   h.clear();
-  let snapshot = Telemetry.histogramSnapshots.parent;
-  let subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.ok(!(COUNT in snapshot));
-  Assert.ok(!(COUNT in subsession));
+  let snapshot = Telemetry.histogramSnapshots;
+  let subsession = Telemetry.snapshotSubsessionHistograms();
+  Assert.ok(!(ID in snapshot));
+  Assert.ok(!(ID in subsession));
 
   // They should instantiate and pick-up the count.
   h.add(1);
-  snapshot = Telemetry.histogramSnapshots.parent;
-  subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.ok(COUNT in snapshot);
-  Assert.ok(COUNT in subsession);
-  Assert.equal(snapshot[COUNT].sum, 1);
-  Assert.equal(subsession[COUNT].sum, 1);
+  snapshot = Telemetry.histogramSnapshots;
+  subsession = Telemetry.snapshotSubsessionHistograms();
+  Assert.ok(ID in snapshot);
+  Assert.ok(ID in subsession);
+  Assert.equal(snapshot[ID].sum, 1);
+  Assert.equal(subsession[ID].sum, 1);
 
   // They should still reset properly.
   h.clear();
-  snapshot = Telemetry.histogramSnapshots.parent;
-  subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.ok(!(COUNT in snapshot));
-  Assert.ok(!(COUNT in subsession));
+  snapshot = Telemetry.histogramSnapshots;
+  subsession = Telemetry.snapshotSubsessionHistograms();
+  Assert.ok(!(ID in snapshot));
+  Assert.ok(!(ID in subsession));
 
   // Both should instantiate and pick-up the count.
   h.add(1);
-  snapshot = Telemetry.histogramSnapshots.parent;
-  subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.ok(COUNT in snapshot);
-  Assert.ok(COUNT in subsession);
-  Assert.equal(snapshot[COUNT].sum, 1);
-  Assert.equal(subsession[COUNT].sum, 1);
+  snapshot = Telemetry.histogramSnapshots;
+  subsession = Telemetry.snapshotSubsessionHistograms();
+  Assert.equal(snapshot[ID].sum, 1);
+  Assert.equal(subsession[ID].sum, 1);
 
   // Check that we are able to only reset the duplicate histogram.
   h.clear(true);
-  snapshot = Telemetry.histogramSnapshots.parent;
-  subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.ok(COUNT in snapshot);
-  Assert.ok(!(COUNT in subsession));
-  Assert.equal(snapshot[COUNT].sum, 1);
+  snapshot = Telemetry.histogramSnapshots;
+  subsession = Telemetry.snapshotSubsessionHistograms();
+  Assert.ok(ID in snapshot);
+  Assert.ok(ID in subsession);
+  Assert.equal(snapshot[ID].sum, 1);
+  Assert.equal(subsession[ID].sum, 0);
 
   // Both should register the next count.
   h.add(1);
-  snapshot = Telemetry.histogramSnapshots.parent;
-  subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.equal(snapshot[COUNT].sum, 2);
-  Assert.equal(subsession[COUNT].sum, 1);
+  snapshot = Telemetry.histogramSnapshots;
+  subsession = Telemetry.snapshotSubsessionHistograms();
+  Assert.equal(snapshot[ID].sum, 2);
+  Assert.equal(subsession[ID].sum, 1);
 
   // Retrieve a subsession snapshot and pass the flag to
   // clear subsession histograms too.
   h.clear();
   flag.clear();
   h.add(1);
   flag.add(1);
-  snapshot = Telemetry.histogramSnapshots.parent;
-  subsession = Telemetry.snapshotSubsessionHistograms(true).parent;
-  Assert.ok(COUNT in snapshot);
-  Assert.ok(COUNT in subsession);
+  snapshot = Telemetry.histogramSnapshots;
+  subsession = Telemetry.snapshotSubsessionHistograms(true);
+  Assert.ok(ID in snapshot);
+  Assert.ok(ID in subsession);
   Assert.ok(FLAG in snapshot);
   Assert.ok(FLAG in subsession);
-  Assert.equal(snapshot[COUNT].sum, 1);
-  Assert.equal(subsession[COUNT].sum, 1);
+  Assert.equal(snapshot[ID].sum, 1);
+  Assert.equal(subsession[ID].sum, 1);
   Assert.equal(snapshot[FLAG].sum, 1);
   Assert.equal(subsession[FLAG].sum, 1);
 
   // The next subsesssion snapshot should show the histograms
   // got reset.
-  snapshot = Telemetry.histogramSnapshots.parent;
-  subsession = Telemetry.snapshotSubsessionHistograms().parent;
-  Assert.ok(COUNT in snapshot);
-  Assert.ok(!(COUNT in subsession));
+  snapshot = Telemetry.histogramSnapshots;
+  subsession = Telemetry.snapshotSubsessionHistograms();
+  Assert.ok(ID in snapshot);
+  Assert.ok(ID in subsession);
   Assert.ok(FLAG in snapshot);
   Assert.ok(FLAG in subsession);
-  Assert.equal(snapshot[COUNT].sum, 1);
+  Assert.equal(snapshot[ID].sum, 1);
+  Assert.equal(subsession[ID].sum, 0);
   Assert.equal(snapshot[FLAG].sum, 1);
   Assert.equal(subsession[FLAG].sum, 0);
 });
 
 add_task({
   skip_if: () => gIsAndroid
 },
 function test_keyed_subsession() {
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -774,44 +774,44 @@ add_task(async function test_checkSubses
     "TELEMETRY_TEST_KEYED_RELEASE_OPTOUT",
   ]);
 
   // Compare the two sets of histograms.
   // The "subsession" histograms should match the registered
   // "classic" histograms. However, histograms can change
   // between us collecting the different payloads, so we only
   // check for deep equality on known stable histograms.
-  let checkHistograms = (classic, subsession, message) => {
-    for (let id of Object.keys(subsession)) {
+  let checkHistograms = (classic, subsession) => {
+    for (let id of Object.keys(classic)) {
       if (!registeredIds.has(id)) {
         continue;
       }
 
-      Assert.ok(id in classic, message + ` (${id})`);
+      Assert.ok(id in subsession);
       if (stableHistograms.has(id)) {
         Assert.deepEqual(classic[id],
-                         subsession[id], message);
+                         subsession[id]);
       } else {
         Assert.equal(classic[id].histogram_type,
-                     subsession[id].histogram_type, message);
+                     subsession[id].histogram_type);
       }
     }
   };
 
   // Same as above, except for keyed histograms.
-  let checkKeyedHistograms = (classic, subsession, message) => {
-    for (let id of Object.keys(subsession)) {
+  let checkKeyedHistograms = (classic, subsession) => {
+    for (let id of Object.keys(classic)) {
       if (!registeredIds.has(id)) {
         continue;
       }
 
-      Assert.ok(id in classic, message);
+      Assert.ok(id in subsession);
       if (stableKeyedHistograms.has(id)) {
         Assert.deepEqual(classic[id],
-                         subsession[id], message);
+                         subsession[id]);
       }
     }
   };
 
   // Both classic and subsession payload histograms should start the same.
   // The payloads should be identical for now except for the reason.
   count.clear();
   keyed.clear();
@@ -820,89 +820,90 @@ add_task(async function test_checkSubses
 
   Assert.equal(classic.info.reason, "gather-payload");
   Assert.equal(subsession.info.reason, "environment-change");
   Assert.ok(!(COUNT_ID in classic.histograms));
   Assert.ok(!(COUNT_ID in subsession.histograms));
   Assert.ok(!(KEYED_ID in classic.keyedHistograms));
   Assert.ok(!(KEYED_ID in subsession.keyedHistograms));
 
-  checkHistograms(classic.histograms, subsession.histograms, "Should start the same");
-  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms, "Keyed should start the same");
+  checkHistograms(classic.histograms, subsession.histograms);
+  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
 
   // Adding values should get picked up in both.
   count.add(1);
   keyed.add("a", 1);
   keyed.add("b", 1);
   classic = TelemetrySession.getPayload();
   subsession = TelemetrySession.getPayload("environment-change");
 
   Assert.ok(COUNT_ID in classic.histograms);
   Assert.ok(COUNT_ID in subsession.histograms);
   Assert.ok(KEYED_ID in classic.keyedHistograms);
   Assert.ok(KEYED_ID in subsession.keyedHistograms);
   Assert.equal(classic.histograms[COUNT_ID].sum, 1);
   Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
   Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
 
-  checkHistograms(classic.histograms, subsession.histograms, "Added values should be picked up");
-  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms, "Added values should be picked up by keyed");
+  checkHistograms(classic.histograms, subsession.histograms);
+  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
 
   // Values should still reset properly.
   count.clear();
   keyed.clear();
   classic = TelemetrySession.getPayload();
   subsession = TelemetrySession.getPayload("environment-change");
 
   Assert.ok(!(COUNT_ID in classic.histograms));
   Assert.ok(!(COUNT_ID in subsession.histograms));
   Assert.ok(!(KEYED_ID in classic.keyedHistograms));
   Assert.ok(!(KEYED_ID in subsession.keyedHistograms));
 
-  checkHistograms(classic.histograms, subsession.histograms, "Values should reset");
-  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms, "Keyed values should reset");
+  checkHistograms(classic.histograms, subsession.histograms);
+  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
 
   // Adding values should get picked up in both.
   count.add(1);
   keyed.add("a", 1);
   keyed.add("b", 1);
   classic = TelemetrySession.getPayload();
   subsession = TelemetrySession.getPayload("environment-change");
 
   Assert.ok(COUNT_ID in classic.histograms);
   Assert.ok(COUNT_ID in subsession.histograms);
   Assert.ok(KEYED_ID in classic.keyedHistograms);
   Assert.ok(KEYED_ID in subsession.keyedHistograms);
   Assert.equal(classic.histograms[COUNT_ID].sum, 1);
   Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
   Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
 
-  checkHistograms(classic.histograms, subsession.histograms, "Adding values should be picked up again");
-  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms, "Adding values should be picked up by keyed again");
+  checkHistograms(classic.histograms, subsession.histograms);
+  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
 
   // We should be able to reset only the subsession histograms.
   // First check that "snapshot and clear" still returns the old state...
   classic = TelemetrySession.getPayload();
   subsession = TelemetrySession.getPayload("environment-change", true);
 
   let subsessionStartDate = new Date(classic.info.subsessionStartDate);
   Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString());
   subsessionStartDate = new Date(subsession.info.subsessionStartDate);
   Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString());
-  checkHistograms(classic.histograms, subsession.histograms, "Should be able to reset subsession");
-  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms, "Should be able to reset subsession keyed");
+  checkHistograms(classic.histograms, subsession.histograms);
+  checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
 
   // ... then check that the next snapshot shows the subsession
   // histograms got reset.
   classic = TelemetrySession.getPayload();
   subsession = TelemetrySession.getPayload("environment-change");
 
   Assert.ok(COUNT_ID in classic.histograms);
-  Assert.ok(!(COUNT_ID in subsession.histograms));
+  Assert.ok(COUNT_ID in subsession.histograms);
   Assert.equal(classic.histograms[COUNT_ID].sum, 1);
+  Assert.equal(subsession.histograms[COUNT_ID].sum, 0);
 
   Assert.ok(KEYED_ID in classic.keyedHistograms);
   Assert.ok(!(KEYED_ID in subsession.keyedHistograms));
   Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
   Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
 
   // Adding values should get picked up in both again.
   count.add(1);
@@ -1044,17 +1045,17 @@ add_task(async function test_dailyCollec
   ping = await PingServer.promiseNextPing();
   Assert.ok(!!ping);
 
   Assert.equal(ping.type, PING_TYPE_MAIN);
   Assert.equal(ping.payload.info.reason, REASON_DAILY);
   subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
   Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString());
 
-  Assert.ok(!(COUNT_ID in ping.payload.histograms));
+  Assert.equal(ping.payload.histograms[COUNT_ID].sum, 0);
   Assert.ok(!(KEYED_ID in ping.payload.keyedHistograms));
 
   // Trigger and collect another daily ping, with the histograms being set again.
   count.add(1);
   keyed.add("a", 1);
   keyed.add("b", 1);
 
   // The daily ping is rescheduled for "tomorrow".
@@ -1248,17 +1249,17 @@ add_task(async function test_environment
   Assert.ok(!!ping);
 
   Assert.equal(ping.type, PING_TYPE_MAIN);
   Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], 1);
   Assert.equal(ping.payload.info.reason, REASON_ENVIRONMENT_CHANGE);
   subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
   Assert.equal(subsessionStartDate.toISOString(), startHour.toISOString());
 
-  Assert.ok(!(COUNT_ID in ping.payload.histograms));
+  Assert.equal(ping.payload.histograms[COUNT_ID].sum, 0);
   Assert.ok(!(KEYED_ID in ping.payload.keyedHistograms));
 
   await TelemetryController.testShutdown();
 });
 
 add_task(async function test_experimentAnnotations_subsession() {
   if (gIsAndroid) {
     // We don't split subsessions on environment changes yet on Android.
--- a/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js
+++ b/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js
@@ -28,17 +28,17 @@ var HISTOGRAMS = {
 add_task(async function init() {
   do_get_profile();
   PATH = Path.join(Constants.Path.localProfileDir, "ShutdownDuration.json");
 });
 
 add_task(async function test_reload() {
   do_print("Forging data");
   let data = {};
-  let telemetrySnapshots = Services.telemetry.histogramSnapshots.parent;
+  let telemetrySnapshots = Services.telemetry.histogramSnapshots;
   let i = 0;
   for (let k of Object.keys(HISTOGRAMS)) {
     let id = HISTOGRAMS[k];
     data[k] = i++;
     Assert.equal(telemetrySnapshots[id] || undefined, undefined, "Histogram " + id + " is empty");
   }
 
 
@@ -59,17 +59,17 @@ add_task(async function test_reload() {
   let tt = Cc["@mozilla.org/toolkit/shutdown-terminator-telemetry;1"].
     createInstance(Ci.nsIObserver);
   tt.observe(null, "profile-after-change", "");
 
   do_print("Waiting until telemetry is updated");
   // Now wait until Telemetry is updated
   await wait;
 
-  telemetrySnapshots = Services.telemetry.histogramSnapshots.parent;
+  telemetrySnapshots = Services.telemetry.histogramSnapshots;
   for (let k of Object.keys(HISTOGRAMS)) {
     let id = HISTOGRAMS[k];
     do_print("Testing histogram " + id);
     let snapshot = telemetrySnapshots[id];
     let count = 0;
     for (let x of snapshot.counts) {
       count += x;
     }
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -4617,28 +4617,47 @@ void XRE_GlibInit()
     // g_type_init (2.32 <= gLib version < 2.36)."
     g_thread_init(nullptr);
     g_type_init();
     ran_once = true;
   }
 }
 #endif
 
+// Separate stub function to let us specifically suppress it in Valgrind
+void
+XRE_CreateStatsObject()
+{
+  // Initialize global variables used by histogram collection
+  // machinery that is used by by Telemetry.  Note: is never de-initialised.
+  Telemetry::CreateStatisticsRecorder();
+}
+
 /*
  * XRE_main - A class based main entry point used by most platforms.
  *            Note that on OSX, aAppData->xreDirectory will point to
  *            .app/Contents/Resources.
  */
 int
 XREMain::XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig)
 {
   ScopedLogging log;
 
   mozilla::LogModule::Init();
 
+  // NB: this must happen after the creation of |ScopedLogging log| since
+  // ScopedLogging::ScopedLogging calls NS_LogInit, and
+  // XRE_CreateStatsObject calls Telemetry::CreateStatisticsRecorder,
+  // and NS_LogInit must be called before Telemetry::CreateStatisticsRecorder.
+  // NS_LogInit must be called before Telemetry::CreateStatisticsRecorder
+  // so as to avoid many log messages of the form
+  //   WARNING: XPCOM objects created/destroyed from static ctor/dtor: [..]
+  // See bug 1279614.
+  XRE_CreateStatsObject();
+
 #if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID)
   SandboxInfo::ThreadingCheck();
 #endif
 
 #ifdef MOZ_CODE_COVERAGE
   CodeCoverageHandler::Init();
 #endif
 
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -75,16 +75,18 @@
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/WindowsDllBlocklist.h"
 
 #include "GMPProcessChild.h"
 #include "mozilla/gfx/GPUProcessImpl.h"
 
 #include "GeckoProfiler.h"
 
+#include "mozilla/Telemetry.h"
+
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #include "mozilla/sandboxTarget.h"
 #include "mozilla/sandboxing/loggingCallbacks.h"
 #endif
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #include "mozilla/SandboxSettings.h"
 #if !defined(MOZ_WIDGET_GONK)
@@ -402,16 +404,24 @@ XRE_InitChildProcess(int aArgc,
     SandboxTarget::Instance()->SetTargetServices(aChildData->sandboxTargetServices);
   }
 #endif
 #endif
 
   // NB: This must be called before profiler_init
   ScopedLogging logger;
 
+  // This is needed by Telemetry to initialize histogram collection.
+  // NB: This must be called after NS_LogInit().
+  // NS_LogInit must be called before Telemetry::CreateStatisticsRecorder
+  // so as to avoid many log messages of the form
+  //   WARNING: XPCOM objects created/destroyed from static ctor/dtor: [..]
+  // See bug 1279614.
+  Telemetry::CreateStatisticsRecorder();
+
   mozilla::LogModule::Init();
 
   char aLocal;
   AutoProfilerInit profilerInit(&aLocal);
 
   AUTO_PROFILER_LABEL("XRE_InitChildProcess", OTHER);
 
   // Ensure AbstractThread is minimally setup, so async IPC messages
@@ -715,16 +725,17 @@ XRE_InitChildProcess(int aArgc,
 #if defined(XP_WIN) && !defined(DEBUG)
   // XXX Bug 1320134: added for diagnosing the crashes because we're running out
   // of TLS indices on Windows. Remove after the root cause is found.
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     mozilla::ShutdownTlsAllocationTracker();
   }
 #endif
 
+  Telemetry::DestroyStatisticsRecorder();
   return XRE_DeinitCommandLine();
 }
 
 MessageLoop*
 XRE_GetIOMessageLoop()
 {
   if (sChildProcessType == GeckoProcessType_Default) {
     return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO);