Backed out changeset f7481a586899 (bug 1258183)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 03 Jun 2016 15:14:51 +0200
changeset 375191 2462c9d05dbd7916593abc022cbe427f92ad30d4
parent 375132 1330c510901a9c6b574532af366f0e9acb8beb8c
child 375192 d6070dd805cf06ff2be62b95b34adce09ace2061
push id20189
push userjlund@mozilla.com
push dateFri, 03 Jun 2016 17:40:55 +0000
bugs1258183
milestone49.0a1
backs outf7481a58689996570832ff30312eff1d57567b77
Backed out changeset f7481a586899 (bug 1258183)
ipc/chromium/src/base/histogram.cc
ipc/chromium/src/base/histogram.h
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/Telemetry.h
toolkit/components/telemetry/TelemetryHistogram.cpp
toolkit/components/telemetry/TelemetryHistogram.h
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
--- a/ipc/chromium/src/base/histogram.cc
+++ b/ipc/chromium/src/base/histogram.cc
@@ -24,16 +24,18 @@
 namespace base {
 
 #define DVLOG(x) CHROMIUM_LOG(ERROR)
 #define CHECK_GT DCHECK_GT
 #define CHECK_LT DCHECK_LT
 typedef ::Lock Lock;
 typedef ::AutoLock AutoLock;
 
+using mozilla::OffTheBooksMutexAutoLock;
+
 // Static table of checksums for all possible 8 bit bytes.
 const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
 0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL,
 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL,
 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L,
 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL,
@@ -170,60 +172,64 @@ void Histogram::WriteHTMLGraph(std::stri
 
 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();
+  // For the rest of the routine, we hold |snapshot|'s lock so as to
+  // be able to examine it atomically.
+  OffTheBooksMutexAutoLock locker(snapshot.mutex());
 
-  WriteAsciiHeader(snapshot, sample_count, output);
+  Count sample_count = snapshot.TotalCount(locker);
+
+  WriteAsciiHeader(snapshot, locker, 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);
+    max_size = GetPeakBucketSize(snapshot, locker);
 
   // 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)) {
+  while (0 == snapshot.counts(locker, 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)) {
+    if (snapshot.counts(locker, 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);
+    Count current = snapshot.counts(locker, 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 < bucket_count() - 1 && 0 == snapshot.counts(locker, i + 1)) {
+      while (i < bucket_count() - 1 && 0 == snapshot.counts(locker, 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);
@@ -233,34 +239,34 @@ void Histogram::WriteAscii(bool graph_it
   }
   DCHECK_EQ(sample_count, past);
 }
 
 //------------------------------------------------------------------------------
 // Methods for the validating a sample and a related histogram.
 //------------------------------------------------------------------------------
 
-Histogram::Inconsistencies
-Histogram::FindCorruption(const SampleSet& snapshot) const
-{
+Histogram::Inconsistencies Histogram::FindCorruption(
+    const SampleSet& snapshot,
+    const OffTheBooksMutexAutoLock& snapshotLockEvidence) const {
   int inconsistencies = NO_INCONSISTENCIES;
   Sample previous_range = -1;  // Bottom range is always 0.
   int64_t count = 0;
   for (size_t index = 0; index < bucket_count(); ++index) {
-    count += snapshot.counts(index);
+    count += snapshot.counts(snapshotLockEvidence, index);
     int new_range = ranges(index);
     if (previous_range >= new_range)
       inconsistencies |= BUCKET_ORDER_ERROR;
     previous_range = new_range;
   }
 
   if (!HasValidRangeChecksum())
     inconsistencies |= RANGE_CHECKSUM_ERROR;
 
-  int64_t delta64 = snapshot.redundant_count() - count;
+  int64_t delta64 = snapshot.redundant_count(snapshotLockEvidence) - count;
   if (delta64 != 0) {
     int delta = static_cast<int>(delta64);
     if (delta != delta64)
       delta = INT_MAX;  // Flag all giant errors as INT_MAX.
     // Since snapshots of histograms are taken asynchronously relative to
     // sampling (and snapped from different threads), it is pretty likely that
     // 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
@@ -291,16 +297,17 @@ Histogram::Sample Histogram::ranges(size
   return ranges_[i];
 }
 
 size_t Histogram::bucket_count() const {
   return bucket_count_;
 }
 
 void Histogram::SnapshotSample(SampleSet* sample) const {
+  OffTheBooksMutexAutoLock locker(sample_.mutex());
   *sample = sample_;
 }
 
 bool Histogram::HasConstructorArguments(Sample minimum,
                                         Sample maximum,
                                         size_t bucket_count) {
   return ((minimum == declared_min_) && (maximum == declared_max_) &&
           (bucket_count == bucket_count_));
@@ -324,19 +331,19 @@ size_t Histogram::SizeOfIncludingThis(mo
   n += aMallocSizeOf(this);
   // We're not allowed to do deep dives into STL data structures.  This
   // is as close as we can get to measuring this array.
   n += aMallocSizeOf(&ranges_[0]);
   n += sample_.SizeOfExcludingThis(aMallocSizeOf);
   return n;
 }
 
-size_t
-Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+size_t Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
 {
+  OffTheBooksMutexAutoLock locker(mutex_);
   // 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(const std::string& name, Sample minimum,
                      Sample maximum, size_t bucket_count)
   : sample_(),
@@ -544,35 +551,39 @@ uint32_t Histogram::Crc32(uint32_t sum, 
     sum += sum >> 11;
   }
   return sum;
 }
 
 //------------------------------------------------------------------------------
 // Private methods
 
-double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
+double Histogram::GetPeakBucketSize(const SampleSet& snapshot,
+                                    const OffTheBooksMutexAutoLock&
+                                          snapshotLockEvidence) const {
   double max = 0;
   for (size_t i = 0; i < bucket_count() ; ++i) {
     double current_size
-        = GetBucketSize(snapshot.counts(i), i);
+        = GetBucketSize(snapshot.counts(snapshotLockEvidence, i), i);
     if (current_size > max)
       max = current_size;
   }
   return max;
 }
 
 void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
+                                 const OffTheBooksMutexAutoLock&
+                                       snapshotLockEvidence,
                                  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();
+  int64_t snapshot_sum = snapshot.sum(snapshotLockEvidence);
   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)
@@ -613,48 +624,60 @@ void Histogram::WriteAsciiBucketGraph(do
 
 //------------------------------------------------------------------------------
 // Methods for the Histogram::SampleSet class
 //------------------------------------------------------------------------------
 
 Histogram::SampleSet::SampleSet()
     : counts_(),
       sum_(0),
-      redundant_count_(0) {
+      redundant_count_(0),
+      mutex_("Histogram::SampleSet::SampleSet") {
 }
 
 Histogram::SampleSet::~SampleSet() {
 }
 
 void Histogram::SampleSet::Resize(const Histogram& histogram) {
+  OffTheBooksMutexAutoLock locker(mutex_);
   counts_.resize(histogram.bucket_count(), 0);
 }
 
-void Histogram::SampleSet::Accumulate(Sample value, Count count,
+void Histogram::SampleSet::Accumulate(const OffTheBooksMutexAutoLock& ev,
+                                      Sample value, Count count,
                                       size_t index) {
   DCHECK(count == 1 || count == -1);
   counts_[index] += count;
   redundant_count_ += count;
   sum_ += static_cast<int64_t>(count) * value;
   DCHECK_GE(counts_[index], 0);
   DCHECK_GE(sum_, 0);
   DCHECK_GE(redundant_count_, 0);
 }
 
-Count Histogram::SampleSet::TotalCount() const {
+void Histogram::SampleSet::Accumulate(Sample value,
+                                      Count count,
+                                      size_t index) {
+  OffTheBooksMutexAutoLock locker(mutex_);
+  Accumulate(locker, value, count, index);
+}
+
+Count Histogram::SampleSet::TotalCount(const OffTheBooksMutexAutoLock& ev)
+                                                                       const {
   Count total = 0;
   for (Counts::const_iterator it = counts_.begin();
        it != counts_.end();
        ++it) {
     total += *it;
   }
   return total;
 }
 
 void Histogram::SampleSet::Add(const SampleSet& other) {
+  OffTheBooksMutexAutoLock locker(mutex_);
   DCHECK_EQ(counts_.size(), other.counts_.size());
   sum_ += other.sum_;
   redundant_count_ += other.redundant_count_;
   for (size_t index = 0; index < counts_.size(); ++index)
     counts_[index] += other.counts_[index];
 }
 
 //------------------------------------------------------------------------------
@@ -846,36 +869,37 @@ FlagHistogram::Accumulate(Sample value, 
   DCHECK_EQ(value, 1);
   LinearHistogram::Accumulate(value, 1, index);
   size_t zero_index = BucketIndex(0);
   LinearHistogram::Accumulate(0, -1, zero_index);
 }
 
 void
 FlagHistogram::AddSampleSet(const SampleSet& sample) {
-  DCHECK_EQ(bucket_count(), sample.size());
+  OffTheBooksMutexAutoLock locker(sample.mutex());
+  DCHECK_EQ(bucket_count(), sample.size(locker));
   // We can't be sure the SampleSet provided came from another FlagHistogram,
   // so we take the following steps:
   //  - If our flag has already been set do nothing.
   //  - Set our flag if the following hold:
   //      - The sum of the counts in the provided SampleSet is 1.
   //      - The bucket index for that single value is the same as the index where we
   //        would place our set flag.
   //  - Otherwise, take no action.
 
   if (mSwitched) {
     return;
   }
 
-  if (sample.sum() != 1) {
+  if (sample.sum(locker) != 1) {
     return;
   }
 
   size_t one_index = BucketIndex(1);
-  if (sample.counts(one_index) == 1) {
+  if (sample.counts(locker, one_index) == 1) {
     Accumulate(1, 1, one_index);
   }
 }
 
 void
 FlagHistogram::Clear() {
   Histogram::Clear();
 
@@ -917,28 +941,30 @@ void
 CountHistogram::Accumulate(Sample value, Count count, size_t index)
 {
   size_t zero_index = BucketIndex(0);
   LinearHistogram::Accumulate(value, 1, zero_index);
 }
 
 void
 CountHistogram::AddSampleSet(const SampleSet& sample) {
-  DCHECK_EQ(bucket_count(), sample.size());
+  OffTheBooksMutexAutoLock locker(sample.mutex());
+  DCHECK_EQ(bucket_count(), sample.size(locker));
   // We can't be sure the SampleSet provided came from another CountHistogram,
   // so we at least check that the unused buckets are empty.
 
   const size_t indices[] = { BucketIndex(0), BucketIndex(1), BucketIndex(2) };
 
-  if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) {
+  if (sample.counts(locker, indices[1]) != 0 ||
+      sample.counts(locker, indices[2]) != 0) {
     return;
   }
 
-  if (sample.counts(indices[0]) != 0) {
-    Accumulate(1, sample.counts(indices[0]), indices[0]);
+  if (sample.counts(locker, indices[0]) != 0) {
+    Accumulate(1, sample.counts(locker, indices[0]), indices[0]);
   }
 }
 
 
 //------------------------------------------------------------------------------
 // CustomHistogram:
 //------------------------------------------------------------------------------
 
--- a/ipc/chromium/src/base/histogram.h
+++ b/ipc/chromium/src/base/histogram.h
@@ -40,26 +40,30 @@
 // pointer is NOT deleted, and we leak the histograms at process termination.
 
 #ifndef BASE_METRICS_HISTOGRAM_H_
 #define BASE_METRICS_HISTOGRAM_H_
 #pragma once
 
 #include "mozilla/Atomics.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
 
 #include <map>
 #include <string>
 #include <vector>
 
 #include "base/time.h"
 #include "base/lock.h"
 
 namespace base {
 
+using mozilla::OffTheBooksMutex;
+using mozilla::OffTheBooksMutexAutoLock;
+
 //------------------------------------------------------------------------------
 // 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)
 
@@ -318,44 +322,74 @@ class Histogram {
   //----------------------------------------------------------------------------
   // Statistic values, developed over the life of the histogram.
 
   class SampleSet {
    public:
     explicit SampleSet();
     ~SampleSet();
 
-    // None of the methods in this class are thread-safe.  Callers
-    // must deal with locking themselves.
+    // This class contains a mozilla::OffTheBooksMutex, |mutex_|.
+    // Most of the methods are thread-safe: they acquire and release
+    // the mutex themselves.  A few are not thread-safe, and require
+    // the caller to provide evidence that the object is locked, by
+    // supplying a const OffTheBooksMutexAutoLock& parameter.  The
+    // parameter is ignored but must be present.  |mutex_| must be an
+    // OffTheBooks variant because some of the containing SampleSet
+    // objects are leaked until shutdown, so a standard Mutex can't be
+    // used, since that does leak checking, and causes test failures.
+
+    //---------------- THREAD SAFE METHODS ----------------//
+    //
+    // The caller must not already hold |this.mutex_|, otherwise we
+    // will end up deadlocking.
 
     // Adjust size of counts_ for use with given histogram.
     void Resize(const Histogram& histogram);
 
     // Accessor for histogram to make routine additions.
     void Accumulate(Sample value, Count count, size_t index);
 
     // Arithmetic manipulation of corresponding elements of the set.
     void Add(const SampleSet& other);
 
     size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
-    Count counts(size_t i) const {
+    //---------------- THREAD UNSAFE METHODS ----------------//
+    //
+    // The caller must hold |this.mutex_|, and must supply evidence by passing
+    // a const reference to the relevant OffTheBooksMutexAutoLock used.
+
+    Count counts(const OffTheBooksMutexAutoLock& ev, size_t i) const {
        return counts_[i];
     }
-    Count TotalCount() const;
-    int64_t sum() const {
+    Count TotalCount(const OffTheBooksMutexAutoLock& ev) const;
+    int64_t sum(const OffTheBooksMutexAutoLock& ev) const {
        return sum_;
     }
-    int64_t redundant_count() const {
+    int64_t redundant_count(const OffTheBooksMutexAutoLock& ev) const {
        return redundant_count_;
     }
-    size_t size() const {
+    size_t size(const OffTheBooksMutexAutoLock& ev) const {
        return counts_.size();
     }
 
+    // An assignment operator.  The presence of mozilla::OffTheBooksMutex
+    // in this class causes the default assignment operator to be deleted.
+    const SampleSet& operator=(const SampleSet& other) {
+       counts_          = other.counts_;
+       sum_             = other.sum_;
+       redundant_count_ = other.redundant_count_;
+       return *this;
+    }
+
+   private:
+    void Accumulate(const OffTheBooksMutexAutoLock& ev,
+                    Sample value, Count count, size_t index);
+
    protected:
     // Actual histogram data is stored in buckets, showing the count of values
     // that fit into each bucket.
     Counts counts_;
 
     // Save simple stats locally.  Note that this MIGHT get done in base class
     // without shared memory at some point.
     int64_t sum_;         // sum of samples.
@@ -363,16 +397,23 @@ class Histogram {
     // To help identify memory corruption, we reduntantly save the number of
     // samples we've accumulated into all of our buckets.  We can compare this
     // count to the sum of the counts in all buckets, and detect problems.  Note
     // that due to races in histogram accumulation (if a histogram is indeed
     // updated on several threads simultaneously), the tallies might mismatch,
     // and also the snapshotting code may asynchronously get a mismatch (though
     // generally either race based mismatch cause is VERY rare).
     int64_t redundant_count_;
+
+   private:
+    // Protects all data fields.
+    mutable OffTheBooksMutex mutex_;
+
+   public:
+    OffTheBooksMutex& mutex() const { return mutex_; }
   };
 
   //----------------------------------------------------------------------------
   // minimum should start from 1. 0 is invalid as a minimum. 0 is an implicit
   // default underflow bucket.
   static Histogram* FactoryGet(const std::string& name,
                                Sample minimum,
                                Sample maximum,
@@ -420,32 +461,34 @@ class Histogram {
   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
   // consistent with the bucket ranges and checksums in our histogram.  This can
   // produce a false-alarm if a race occurred in the reading of the data during
   // 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;
+  virtual Inconsistencies FindCorruption(const SampleSet& snapshot,
+                                         const OffTheBooksMutexAutoLock&
+                                               snapshotLockEvidence) 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
-  // of it is done here.
+  // of it is done here.  This routine does lock the source sample though.
   virtual void SnapshotSample(SampleSet* sample) const;
 
   virtual bool HasConstructorArguments(Sample minimum, Sample maximum,
                                        size_t bucket_count);
 
   virtual bool HasConstructorTimeDeltaArguments(TimeDelta minimum,
                                                 TimeDelta maximum,
                                                 size_t bucket_count);
@@ -511,20 +554,23 @@ class Histogram {
 
   // 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;
+  double GetPeakBucketSize(const SampleSet& snapshot,
+                           const OffTheBooksMutexAutoLock&
+                                 snapshotLockEvidence) const;
 
   // Write a common header message describing this histogram.
   void WriteAsciiHeader(const SampleSet& snapshot,
+                        const OffTheBooksMutexAutoLock& snapshotLockEvidence,
                         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;
 
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -2901,20 +2901,10 @@ 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();
-}
-
 } // namespace Telemetry
 } // namespace mozilla
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -33,23 +33,16 @@ namespace HangMonitor {
 namespace Telemetry {
 
 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 TelemetryHistograms.h
  *
  * @param id - histogram id
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -12,81 +12,28 @@
 #include "nsHashKeys.h"
 #include "nsBaseHashtable.h"
 #include "nsClassHashtable.h"
 #include "nsITelemetry.h"
 #include "nsVersionComparator.h"
 
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/StartupTimeline.h"
-#include "mozilla/StaticMutex.h"
 
 #include "TelemetryCommon.h"
 #include "TelemetryHistogram.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;
-
-
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-//
-// Naming: there are two kinds of functions in this file:
-//
-// * Functions named internal_*: these can only be reached via an
-//   interface function (TelemetryHistogram::*).  They mostly expect
-//   the interface function to have acquired
-//   |gTelemetryHistogramMutex|, so they do not have to be
-//   thread-safe.  However, those internal_* functions that are
-//   reachable from internal_WrapAndReturnHistogram and
-//   internal_WrapAndReturnKeyedHistogram can sometimes be called
-//   without |gTelemetryHistogramMutex|, and so might be racey.
-//
-// * Functions named TelemetryHistogram::*.  This is the external interface.
-//   Entries and exits to these functions are serialised using
-//   |gTelemetryHistogramMutex|.
-//
-// Avoiding races and deadlocks:
-//
-// All functions in the external interface (TelemetryHistogram::*) are
-// serialised using the mutex |gTelemetryHistogramMutex|.  This means
-// that the external interface is thread-safe, and many of the
-// internal_* functions can ignore thread safety.  But it also brings
-// a danger of deadlock if any function in the external interface can
-// get back to that interface.  That is, we will deadlock on any call
-// chain like this
-//
-// TelemetryHistogram::* -> .. any functions .. -> TelemetryHistogram::*
-//
-// To reduce the danger of that happening, observe the following rules:
-//
-// * No function in TelemetryHistogram::* may directly call, nor take the
-//   address of, any other function in TelemetryHistogram::*.
-//
-// * No internal function internal_* may call, nor take the address
-//   of, any function in TelemetryHistogram::*.
-//
-// internal_WrapAndReturnHistogram and
-// internal_WrapAndReturnKeyedHistogram are not protected by
-// |gTelemetryHistogramMutex| because they make calls to the JS
-// engine, but that can in turn call back to Telemetry and hence back
-// to a TelemetryHistogram:: function, in order to report GC and other
-// statistics.  This would lead to deadlock due to attempted double
-// acquisition of |gTelemetryHistogramMutex|, if the internal_* functions
-// were required to be protected by |gTelemetryHistogramMutex|.  To
-// break that cycle, we relax that requirement.  Unfortunately this
-// means that this file is not guaranteed race-free.
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE TYPES
 
 #define EXPIRED_ID "__expired__"
@@ -168,144 +115,128 @@ KeyedHistogramMapType gKeyedHistograms;
 
 bool gCorruptHistograms[mozilla::Telemetry::HistogramCount];
 
 // This is for gHistograms, gHistogramStringTable
 #include "TelemetryHistogramData.inc"
 
 AddonMapType gAddonMap;
 
-// 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 mozilla::Telemetry::ID 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
-
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: Misc small helpers
 
 namespace {
 
 bool
-internal_CanRecordBase() {
-  return gCanRecordBase;
-}
-
-bool
-internal_CanRecordExtended() {
-  return gCanRecordExtended;
-}
-
-bool
-internal_IsHistogramEnumId(mozilla::Telemetry::ID aID)
+IsHistogramEnumId(mozilla::Telemetry::ID aID)
 {
   static_assert(((mozilla::Telemetry::ID)-1 > 0), "ID should be unsigned.");
   return aID < mozilla::Telemetry::HistogramCount;
 }
 
 bool
-internal_IsExpired(const char *expiration)
+IsExpired(const char *expiration)
 {
   static mozilla::Version current_version = mozilla::Version(MOZ_APP_VERSION);
   MOZ_ASSERT(expiration);
   return strcmp(expiration, "never") && strcmp(expiration, "default") &&
     (mozilla::Version(expiration) <= current_version);
 }
 
 bool
-internal_IsInDataset(uint32_t dataset, uint32_t containingDataset)
+IsInDataset(uint32_t dataset, uint32_t containingDataset)
 {
   if (dataset == containingDataset) {
     return true;
   }
 
   // The "optin on release channel" dataset is a superset of the
   // "optout on release channel one".
   if (containingDataset == nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN
       && dataset == nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT) {
     return true;
   }
 
   return false;
 }
 
 bool
-internal_CanRecordDataset(uint32_t dataset)
+CanRecordDataset(uint32_t dataset)
 {
   // If we are extended telemetry is enabled, we are allowed to record
   // regardless of the dataset.
-  if (internal_CanRecordExtended()) {
+  if (TelemetryHistogram::CanRecordExtended()) {
     return true;
   }
 
   // If base telemetry data is enabled and we're trying to record base
   // telemetry, allow it.
-  if (internal_CanRecordBase() &&
-      internal_IsInDataset(dataset,
-                           nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT)) {
+  if (TelemetryHistogram::CanRecordBase() &&
+      IsInDataset(dataset, nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT)) {
       return true;
   }
 
   // We're not recording extended telemetry or this is not the base
   // dataset. Bail out.
   return false;
 }
 
 bool
-internal_IsValidHistogramName(const nsACString& name)
+IsValidHistogramName(const nsACString& name)
 {
   return !FindInReadable(NS_LITERAL_CSTRING(KEYED_HISTOGRAM_NAME_SEPARATOR), name);
 }
 
 // Note: this is completely unrelated to mozilla::IsEmpty.
 bool
-internal_IsEmpty(const Histogram *h)
+IsEmpty(const Histogram *h)
 {
   Histogram::SampleSet ss;
   h->SnapshotSample(&ss);
-  return ss.counts(0) == 0 && ss.sum() == 0;
+
+  mozilla::OffTheBooksMutexAutoLock locker(ss.mutex());
+  return ss.counts(locker, 0) == 0 && ss.sum(locker) == 0;
 }
 
 bool
-internal_IsExpired(const Histogram *histogram)
+IsExpired(const Histogram *histogram)
 {
   return histogram->histogram_name() == EXPIRED_ID;
 }
 
 nsresult
-internal_GetRegisteredHistogramIds(bool keyed, uint32_t dataset,
-                                   uint32_t *aCount, char*** aHistograms)
+GetRegisteredHistogramIds(bool keyed, uint32_t dataset, uint32_t *aCount,
+                          char*** aHistograms)
 {
   nsTArray<char*> collection;
 
   for (size_t i = 0; i < mozilla::ArrayLength(gHistograms); ++i) {
     const HistogramInfo& h = gHistograms[i];
-    if (internal_IsExpired(h.expiration()) || h.keyed != keyed ||
-        !internal_IsInDataset(h.dataset, dataset)) {
+    if (IsExpired(h.expiration()) || h.keyed != keyed ||
+        !IsInDataset(h.dataset, dataset)) {
       continue;
     }
 
     const char* id = h.id();
     const size_t len = strlen(id);
     collection.AppendElement(static_cast<char*>(nsMemory::Clone(id, len+1)));
   }
 
@@ -336,19 +267,18 @@ HistogramInfo::expiration() const
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: Histogram Get, Add, Clone, Clear functions
 
 namespace {
 
 nsresult
-internal_CheckHistogramArguments(uint32_t histogramType,
-                                 uint32_t min, uint32_t max,
-                                 uint32_t bucketCount, bool haveOptArgs)
+CheckHistogramArguments(uint32_t histogramType, uint32_t min, uint32_t max,
+                        uint32_t bucketCount, bool haveOptArgs)
 {
   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;
 
@@ -366,28 +296,26 @@ internal_CheckHistogramArguments(uint32_
   return NS_OK;
 }
 
 /*
  * 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)
+HistogramGet(const char *name, const char *expiration, uint32_t histogramType,
+             uint32_t min, uint32_t max, uint32_t bucketCount, bool haveOptArgs,
+             Histogram **result)
 {
-  nsresult rv = internal_CheckHistogramArguments(histogramType, min, max,
-                                                 bucketCount, haveOptArgs);
+  nsresult rv = CheckHistogramArguments(histogramType, min, max, bucketCount, haveOptArgs);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (internal_IsExpired(expiration)) {
+  if (IsExpired(expiration)) {
     name = EXPIRED_ID;
     min = 1;
     max = 2;
     bucketCount = 3;
     histogramType = nsITelemetry::HISTOGRAM_LINEAR;
   }
 
   switch (histogramType) {
@@ -409,55 +337,55 @@ internal_HistogramGet(const char *name, 
   default:
     NS_ASSERTION(false, "Invalid histogram type");
     return NS_ERROR_INVALID_ARG;
   }
   return NS_OK;
 }
 
 nsresult
-internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id)
+GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id)
 {
   if (!gInitDone) {
     return NS_ERROR_FAILURE;
   }
 
   CharPtrEntryType *entry = gHistogramMap.GetEntry(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::ID id, Histogram **ret)
+GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret)
 {
   static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0};
   Histogram *h = knownHistograms[id];
   if (h) {
     *ret = h;
     return NS_OK;
   }
 
   const HistogramInfo &p = gHistograms[id];
   if (p.keyed) {
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv = internal_HistogramGet(p.id(), p.expiration(), p.histogramType,
-                                      p.min, p.max, p.bucketCount, true, &h);
+  nsresult rv = HistogramGet(p.id(), 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 (!internal_IsExpired(p.expiration())) {
+  if (!IsExpired(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");
       }
@@ -465,51 +393,48 @@ internal_GetHistogramByEnumId(mozilla::T
   }
 #endif
 
   *ret = knownHistograms[id] = h;
   return NS_OK;
 }
 
 nsresult
-internal_GetHistogramByName(const nsACString &name, Histogram **ret)
+GetHistogramByName(const nsACString &name, Histogram **ret)
 {
   mozilla::Telemetry::ID id;
-  nsresult rv
-    = internal_GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
+  nsresult rv = GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = internal_GetHistogramByEnumId(id, ret);
+  rv = GetHistogramByEnumId(id, ret);
   if (NS_FAILED(rv))
     return rv;
 
   return NS_OK;
 }
 
 /**
  * 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::ID existingId,
-                        Histogram& existing)
+CloneHistogram(const nsACString& newName, mozilla::Telemetry::ID 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);
+  rv = 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;
   }
 
   Histogram::SampleSet ss;
   existing.SnapshotSample(&ss);
   clone->AddSampleSet(ss);
 
@@ -517,35 +442,33 @@ internal_CloneHistogram(const nsACString
 }
 
 /**
  * This clones a histogram 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::ID existingId)
+CloneHistogram(const nsACString& newName, mozilla::Telemetry::ID existingId)
 {
   Histogram *existing = nullptr;
-  nsresult rv = internal_GetHistogramByEnumId(existingId, &existing);
+  nsresult rv = GetHistogramByEnumId(existingId, &existing);
   if (NS_FAILED(rv)) {
     return nullptr;
   }
 
-  return internal_CloneHistogram(newName, existingId, *existing);
+  return CloneHistogram(newName, existingId, *existing);
 }
 
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 Histogram*
-internal_GetSubsessionHistogram(Histogram& existing)
+GetSubsessionHistogram(Histogram& existing)
 {
   mozilla::Telemetry::ID id;
-  nsresult rv
-    = internal_GetHistogramEnumId(existing.histogram_name().c_str(), &id);
+  nsresult rv = GetHistogramEnumId(existing.histogram_name().c_str(), &id);
   if (NS_FAILED(rv) || gHistograms[id].keyed) {
     return nullptr;
   }
 
   static Histogram* subsession[mozilla::Telemetry::HistogramCount] = {};
   if (subsession[id]) {
     return subsession[id];
   }
@@ -554,124 +477,126 @@ internal_GetSubsessionHistogram(Histogra
   nsDependentCString existingName(gHistograms[id].id());
   if (StringBeginsWith(existingName, prefix)) {
     return nullptr;
   }
 
   nsCString subsessionName(prefix);
   subsessionName.Append(existingName);
 
-  subsession[id] = internal_CloneHistogram(subsessionName, id, existing);
+  subsession[id] = CloneHistogram(subsessionName, id, existing);
   return subsession[id];
 }
 #endif
 
 nsresult
-internal_HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset)
+HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset)
 {
   // Check if we are allowed to record the data.
-  if (!internal_CanRecordDataset(dataset) || !histogram.IsRecordingEnabled()) {
+  if (!CanRecordDataset(dataset) || !histogram.IsRecordingEnabled()) {
     return NS_OK;
   }
 
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
-  if (Histogram* subsession = internal_GetSubsessionHistogram(histogram)) {
+  if (Histogram* subsession = 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)
+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()) {
+  if (!TelemetryHistogram::CanRecordExtended()) {
     mozilla::Telemetry::ID id;
-    nsresult rv
-      = internal_GetHistogramEnumId(histogram.histogram_name().c_str(), &id);
+    nsresult rv = 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);
+  return HistogramAdd(histogram, value, dataset);
 }
 
 void
-internal_HistogramClear(Histogram& aHistogram, bool onlySubsession)
+HistogramClear(Histogram& aHistogram, bool onlySubsession)
 {
   if (!onlySubsession) {
     aHistogram.Clear();
   }
 
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
-  if (Histogram* subsession = internal_GetSubsessionHistogram(aHistogram)) {
+  if (Histogram* subsession = GetSubsessionHistogram(aHistogram)) {
     subsession->Clear();
   }
 #endif
 }
 
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: Histogram corruption helpers
 
 namespace {
 
-void internal_Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample);
-
 void
-internal_IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs)
+IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs)
 {
   for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
     Histogram *h = *it;
 
     mozilla::Telemetry::ID id;
-    nsresult rv = internal_GetHistogramEnumId(h->histogram_name().c_str(), &id);
+    nsresult rv = ::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);
+    Histogram::Inconsistencies check;
+    {
+      mozilla::OffTheBooksMutexAutoLock locker(ss.mutex());
+      check = h->FindCorruption(ss, locker);
+    }
+
     bool corrupt = (check != Histogram::NO_INCONSISTENCIES);
 
     if (corrupt) {
       mozilla::Telemetry::ID 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);
+      TelemetryHistogram::Accumulate(corruptID, 1);
     }
 
     gCorruptHistograms[id] = corrupt;
   }
 }
 
 } // namespace
 
@@ -679,90 +604,90 @@ internal_IdentifyCorruptHistograms(Stati
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: Histogram reflection helpers
 
 namespace {
 
 bool
-internal_FillRanges(JSContext *cx, JS::Handle<JSObject*> array, Histogram *h)
+FillRanges(JSContext *cx, JS::Handle<JSObject*> array, Histogram *h)
 {
   JS::Rooted<JS::Value> range(cx);
   for (size_t i = 0; i < h->bucket_count(); i++) {
     range.setInt32(h->ranges(i));
     if (!JS_DefineElement(cx, array, i, range, JSPROP_ENUMERATE))
       return false;
   }
   return true;
 }
 
 enum reflectStatus
-internal_ReflectHistogramAndSamples(JSContext *cx,
-                                    JS::Handle<JSObject*> obj, Histogram *h,
-                                    const Histogram::SampleSet &ss)
+ReflectHistogramAndSamples(JSContext *cx, JS::Handle<JSObject*> obj, Histogram *h,
+                           const Histogram::SampleSet &ss)
 {
+  mozilla::OffTheBooksMutexAutoLock locker(ss.mutex());
+
   // We don't want to reflect corrupt histograms.
-  if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
+  if (h->FindCorruption(ss, locker) != 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))) {
+                             double(ss.sum(locker)), JSPROP_ENUMERATE))) {
     return REFLECT_FAILURE;
   }
 
   const size_t count = h->bucket_count();
   JS::Rooted<JSObject*> rarray(cx, JS_NewArrayObject(cx, count));
   if (!rarray) {
     return REFLECT_FAILURE;
   }
-  if (!(internal_FillRanges(cx, rarray, h)
+  if (!(FillRanges(cx, rarray, h)
         && JS_DefineProperty(cx, obj, "ranges", rarray, JSPROP_ENUMERATE))) {
     return REFLECT_FAILURE;
   }
 
   JS::Rooted<JSObject*> counts_array(cx, JS_NewArrayObject(cx, count));
   if (!counts_array) {
     return REFLECT_FAILURE;
   }
   if (!JS_DefineProperty(cx, obj, "counts", counts_array, JSPROP_ENUMERATE)) {
     return REFLECT_FAILURE;
   }
   for (size_t i = 0; i < count; i++) {
     if (!JS_DefineElement(cx, counts_array, i,
-                          ss.counts(i), JSPROP_ENUMERATE)) {
+                          ss.counts(locker, i), JSPROP_ENUMERATE)) {
       return REFLECT_FAILURE;
     }
   }
 
   return REFLECT_OK;
 }
 
 enum reflectStatus
-internal_ReflectHistogramSnapshot(JSContext *cx,
-                                  JS::Handle<JSObject*> obj, Histogram *h)
+ReflectHistogramSnapshot(JSContext *cx, JS::Handle<JSObject*> obj, Histogram *h)
 {
   Histogram::SampleSet ss;
   h->SnapshotSample(&ss);
-  return internal_ReflectHistogramAndSamples(cx, obj, h, ss);
+  return ReflectHistogramAndSamples(cx, obj, h, ss);
 }
 
 bool
-internal_ShouldReflectHistogram(Histogram *h)
+ShouldReflectHistogram(Histogram *h)
 {
   const char *name = h->histogram_name().c_str();
   mozilla::Telemetry::ID id;
-  nsresult rv = internal_GetHistogramEnumId(name, &id);
+  nsresult rv = ::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
@@ -770,17 +695,17 @@ internal_ShouldReflectHistogram(Histogra
       return false;
     }
     return true;
   } else {
     return !gCorruptHistograms[id];
   }
 }
 
-} // namespace
+}
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: class KeyedHistogram
 
 namespace {
@@ -867,19 +792,19 @@ KeyedHistogram::GetHistogram(const nsCSt
     histogramName.AppendLiteral(SUBSESSION_HISTOGRAM_PREFIX);
   }
 #endif
   histogramName.Append(mName);
   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);
+  nsresult rv = 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);
@@ -907,17 +832,17 @@ KeyedHistogram::GetDataset(uint32_t* dat
   MOZ_ASSERT(dataset);
   *dataset = mDataset;
   return NS_OK;
 }
 
 nsresult
 KeyedHistogram::Add(const nsCString& key, uint32_t sample)
 {
-  if (!internal_CanRecordDataset(mDataset)) {
+  if (!CanRecordDataset(mDataset)) {
     return NS_OK;
   }
 
   Histogram* histogram = GetHistogram(key, false);
   MOZ_ASSERT(histogram);
   if (!histogram) {
     return NS_ERROR_FAILURE;
   }
@@ -989,18 +914,17 @@ bool
 KeyedHistogram::ReflectKeyedHistogram(KeyedHistogramEntry* entry,
                                       JSContext* cx, JS::Handle<JSObject*> obj)
 {
   JS::RootedObject histogramSnapshot(cx, JS_NewPlainObject(cx));
   if (!histogramSnapshot) {
     return false;
   }
 
-  if (internal_ReflectHistogramSnapshot(cx, histogramSnapshot,
-                                        entry->mData) != REFLECT_OK) {
+  if (ReflectHistogramSnapshot(cx, histogramSnapshot, entry->mData) != REFLECT_OK) {
     return false;
   }
 
   const NS_ConvertUTF8toUTF16 key(entry->GetKey());
   if (!JS_DefineUCProperty(cx, obj, key.Data(), key.Length(),
                            histogramSnapshot, JSPROP_ENUMERATE)) {
     return false;
   }
@@ -1036,17 +960,17 @@ KeyedHistogram::GetJSSnapshot(JSContext*
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: KeyedHistogram helpers
 
 namespace {
 
 KeyedHistogram*
-internal_GetKeyedHistogramById(const nsACString &name)
+GetKeyedHistogramById(const nsACString &name)
 {
   if (!gInitDone) {
     return nullptr;
   }
 
   KeyedHistogram* keyed = nullptr;
   gKeyedHistograms.Get(name, &keyed);
   return keyed;
@@ -1055,36 +979,20 @@ internal_GetKeyedHistogramById(const nsA
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: JSHistogram_* functions
 
-// NOTE: the functions in this section:
-//
-//   internal_JSHistogram_Add
-//   internal_JSHistogram_Snapshot
-//   internal_JSHistogram_Clear
-//   internal_JSHistogram_Dataset
-//   internal_WrapAndReturnHistogram
-//
-// all run without protection from |gTelemetryHistogramMutex|.  If they
-// held |gTelemetryHistogramMutex|, there would be the possibility of
-// 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 {
 
 bool
-internal_JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
+JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   MOZ_ASSERT(h);
@@ -1106,53 +1014,53 @@ internal_JSHistogram_Add(JSContext *cx, 
       return false;
     }
 
     if (!JS::ToInt32(cx, args[0], &value)) {
       return false;
     }
   }
 
-  if (internal_CanRecordBase()) {
-    internal_HistogramAdd(*h, value);
+  if (TelemetryHistogram::CanRecordBase()) {
+    HistogramAdd(*h, value);
   }
 
   return true;
 }
 
 bool
-internal_JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
+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) {
     return false;
   }
 
   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx));
   if (!snapshot)
     return false;
 
-  switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
+  switch (ReflectHistogramSnapshot(cx, snapshot, h)) {
   case REFLECT_FAILURE:
     return false;
   case REFLECT_CORRUPT:
     JS_ReportError(cx, "Histogram is corrupt");
     return false;
   case REFLECT_OK:
     args.rval().setObject(*snapshot);
     return true;
   default:
     MOZ_CRASH("unhandled reflection status");
   }
 }
 
 bool
-internal_JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
+JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   bool onlySubsession = false;
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
@@ -1166,100 +1074,77 @@ internal_JSHistogram_Clear(JSContext *cx
 
     onlySubsession = JS::ToBoolean(args[0]);
   }
 #endif
 
   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   MOZ_ASSERT(h);
   if (h) {
-    internal_HistogramClear(*h, onlySubsession);
+    HistogramClear(*h, onlySubsession);
   }
 
   return true;
 }
 
 bool
-internal_JSHistogram_Dataset(JSContext *cx, unsigned argc, JS::Value *vp)
+JSHistogram_Dataset(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   mozilla::Telemetry::ID id;
-  nsresult rv = internal_GetHistogramEnumId(h->histogram_name().c_str(), &id);
+  nsresult rv = ::GetHistogramEnumId(h->histogram_name().c_str(), &id);
   if (NS_SUCCEEDED(rv)) {
     args.rval().setNumber(gHistograms[id].dataset);
     return true;
   }
 
   return false;
 }
 
-// NOTE: Runs without protection from |gTelemetryHistogramMutex|.
-// See comment at the top of this section.
 nsresult
-internal_WrapAndReturnHistogram(Histogram *h, JSContext *cx,
-                                JS::MutableHandle<JS::Value> ret)
+WrapAndReturnHistogram(Histogram *h, JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
   static const JSClass JSHistogram_class = {
     "JSHistogram",  /* name */
     JSCLASS_HAS_PRIVATE  /* flags */
   };
 
   JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &JSHistogram_class));
   if (!obj)
     return NS_ERROR_FAILURE;
-  // The 4 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)
-        && JS_DefineFunction(cx, obj, "dataset",
-                             internal_JSHistogram_Dataset, 0, 0))) {
+  if (!(JS_DefineFunction(cx, obj, "add", JSHistogram_Add, 1, 0)
+        && JS_DefineFunction(cx, obj, "snapshot", JSHistogram_Snapshot, 0, 0)
+        && JS_DefineFunction(cx, obj, "clear", JSHistogram_Clear, 0, 0)
+        && JS_DefineFunction(cx, obj, "dataset", JSHistogram_Dataset, 0, 0))) {
     return NS_ERROR_FAILURE;
   }
   JS_SetPrivate(obj, h);
   ret.setObject(*obj);
   return NS_OK;
 }
 
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // PRIVATE: JSKeyedHistogram_* functions
 
-// NOTE: the functions in this section:
-//
-//   internal_KeyedHistogram_SnapshotImpl
-//   internal_JSKeyedHistogram_Add
-//   internal_JSKeyedHistogram_Keys
-//   internal_JSKeyedHistogram_Snapshot
-//   internal_JSKeyedHistogram_SubsessionSnapshot
-//   internal_JSKeyedHistogram_SnapshotSubsessionAndClear
-//   internal_JSKeyedHistogram_Clear
-//   internal_JSKeyedHistogram_Dataset
-//   internal_WrapAndReturnKeyedHistogram
-//
-// Same comments as above, at the JSHistogram_* section, regarding
-// deadlock avoidance, apply.
-
 namespace {
 
 bool
-internal_KeyedHistogram_SnapshotImpl(JSContext *cx, unsigned argc,
-                                     JS::Value *vp,
-                                     bool subsession, bool clearSubsession)
+KeyedHistogram_SnapshotImpl(JSContext *cx, unsigned argc, JS::Value *vp,
+                            bool subsession, bool clearSubsession)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
   if (!keyed) {
@@ -1297,32 +1182,32 @@ internal_KeyedHistogram_SnapshotImpl(JSC
     return false;
   }
 
   JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
   if (!snapshot) {
     return false;
   }
 
-  switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
+  switch (ReflectHistogramSnapshot(cx, snapshot, h)) {
   case REFLECT_FAILURE:
     return false;
   case REFLECT_CORRUPT:
     JS_ReportError(cx, "Histogram is corrupt");
     return false;
   case REFLECT_OK:
     args.rval().setObject(*snapshot);
     return true;
   default:
     MOZ_CRASH("unhandled reflection status");
   }
 }
 
 bool
-internal_JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
+JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
   if (!keyed) {
@@ -1362,64 +1247,61 @@ internal_JSKeyedHistogram_Add(JSContext 
     }
   }
 
   keyed->Add(NS_ConvertUTF16toUTF8(key), value);
   return true;
 }
 
 bool
-internal_JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp)
+JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   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));
 }
 
 bool
-internal_JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
+JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
 {
-  return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, false, false);
+  return KeyedHistogram_SnapshotImpl(cx, argc, vp, false, false);
 }
 
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 bool
-internal_JSKeyedHistogram_SubsessionSnapshot(JSContext *cx,
-                                             unsigned argc, JS::Value *vp)
+JSKeyedHistogram_SubsessionSnapshot(JSContext *cx, unsigned argc, JS::Value *vp)
 {
-  return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, true, false);
+  return KeyedHistogram_SnapshotImpl(cx, argc, vp, true, false);
 }
 #endif
 
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 bool
-internal_JSKeyedHistogram_SnapshotSubsessionAndClear(JSContext *cx,
-                                                     unsigned argc,
-                                                     JS::Value *vp)
+JSKeyedHistogram_SnapshotSubsessionAndClear(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     JS_ReportError(cx, "No key arguments supported for snapshotSubsessionAndClear");
   }
 
-  return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, true, true);
+  return KeyedHistogram_SnapshotImpl(cx, argc, vp, true, true);
 }
 #endif
 
 bool
-internal_JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
+JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
   if (!keyed) {
@@ -1442,17 +1324,17 @@ internal_JSKeyedHistogram_Clear(JSContex
   keyed->Clear(onlySubsession);
 #else
   keyed->Clear(false);
 #endif
   return true;
 }
 
 bool
-internal_JSKeyedHistogram_Dataset(JSContext *cx, unsigned argc, JS::Value *vp)
+JSKeyedHistogram_Dataset(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
     return false;
   }
 
   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
@@ -1465,47 +1347,36 @@ internal_JSKeyedHistogram_Dataset(JSCont
   if (NS_FAILED(rv)) {
     return false;
   }
 
   args.rval().setNumber(dataset);
   return true;
 }
 
-// NOTE: Runs without protection from |gTelemetryHistogramMutex|.
-// See comment at the top of this section.
 nsresult
-internal_WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx,
-                                     JS::MutableHandle<JS::Value> ret)
+WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
   static const JSClass JSHistogram_class = {
     "JSKeyedHistogram",  /* name */
     JSCLASS_HAS_PRIVATE  /* flags */
   };
 
   JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &JSHistogram_class));
   if (!obj)
     return NS_ERROR_FAILURE;
-  // The 7 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)
-        && JS_DefineFunction(cx, obj, "snapshot",
-                             internal_JSKeyedHistogram_Snapshot, 1, 0)
+  if (!(JS_DefineFunction(cx, obj, "add", JSKeyedHistogram_Add, 2, 0)
+        && JS_DefineFunction(cx, obj, "snapshot", JSKeyedHistogram_Snapshot, 1, 0)
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
-        && JS_DefineFunction(cx, obj, "subsessionSnapshot",
-                             internal_JSKeyedHistogram_SubsessionSnapshot, 1, 0)
-        && JS_DefineFunction(cx, obj, "snapshotSubsessionAndClear",
-                             internal_JSKeyedHistogram_SnapshotSubsessionAndClear, 0, 0)
+        && JS_DefineFunction(cx, obj, "subsessionSnapshot", JSKeyedHistogram_SubsessionSnapshot, 1, 0)
+        && JS_DefineFunction(cx, obj, "snapshotSubsessionAndClear", JSKeyedHistogram_SnapshotSubsessionAndClear, 0, 0)
 #endif
-        && JS_DefineFunction(cx, obj, "keys",
-                             internal_JSKeyedHistogram_Keys, 0, 0)
-        && JS_DefineFunction(cx, obj, "clear",
-                             internal_JSKeyedHistogram_Clear, 0, 0)
-        && JS_DefineFunction(cx, obj, "dataset",
-                             internal_JSKeyedHistogram_Dataset, 0, 0))) {
+        && JS_DefineFunction(cx, obj, "keys", JSKeyedHistogram_Keys, 0, 0)
+        && JS_DefineFunction(cx, obj, "clear", JSKeyedHistogram_Clear, 0, 0)
+        && JS_DefineFunction(cx, obj, "dataset", JSKeyedHistogram_Dataset, 0, 0))) {
     return NS_ERROR_FAILURE;
   }
 
   JS_SetPrivate(obj, h);
   ret.setObject(*obj);
   return NS_OK;
 }
 
@@ -1519,212 +1390,113 @@ internal_WrapAndReturnKeyedHistogram(Key
 
 namespace {
 
 // Compute the name to pass into Histogram for the addon histogram
 // 'name' from the addon 'id'.  We can't use 'name' directly because it
 // might conflict with other histograms in other addons or even with our
 // own.
 void
-internal_AddonHistogramName(const nsACString &id, const nsACString &name,
-                            nsACString &ret)
+AddonHistogramName(const nsACString &id, const nsACString &name,
+                   nsACString &ret)
 {
   ret.Append(id);
   ret.Append(':');
   ret.Append(name);
 }
 
 bool
-internal_CreateHistogramForAddon(const nsACString &name,
-                                 AddonHistogramInfo &info)
+CreateHistogramForAddon(const nsACString &name, AddonHistogramInfo &info)
 {
   Histogram *h;
-  nsresult rv = internal_HistogramGet(PromiseFlatCString(name).get(), "never",
-                                      info.histogramType, info.min, info.max,
-                                      info.bucketCount, true, &h);
+  nsresult rv = HistogramGet(PromiseFlatCString(name).get(), "never",
+                             info.histogramType, info.min, info.max,
+                             info.bucketCount, true, &h);
   if (NS_FAILED(rv)) {
     return false;
   }
   // Don't let this histogram be reported via the normal means
   // (e.g. Telemetry.registeredHistograms); we'll make it available in
   // other ways.
   h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
   info.h = h;
   return true;
 }
 
 bool
-internal_AddonHistogramReflector(AddonHistogramEntryType *entry,
-                                 JSContext *cx, JS::Handle<JSObject*> obj)
+AddonHistogramReflector(AddonHistogramEntryType *entry,
+                        JSContext *cx, JS::Handle<JSObject*> obj)
 {
   AddonHistogramInfo &info = entry->mData;
 
   // Never even accessed the histogram.
   if (!info.h) {
     // Have to force creation of HISTOGRAM_FLAG histograms.
     if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG)
       return true;
 
-    if (!internal_CreateHistogramForAddon(entry->GetKey(), info)) {
+    if (!CreateHistogramForAddon(entry->GetKey(), info)) {
       return false;
     }
   }
 
-  if (internal_IsEmpty(info.h)) {
+  if (IsEmpty(info.h)) {
     return true;
   }
 
   JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx));
   if (!snapshot) {
     // Just consider this to be skippable.
     return true;
   }
-  switch (internal_ReflectHistogramSnapshot(cx, snapshot, info.h)) {
+  switch (ReflectHistogramSnapshot(cx, snapshot, info.h)) {
   case REFLECT_FAILURE:
   case REFLECT_CORRUPT:
     return false;
   case REFLECT_OK:
     const nsACString &histogramName = entry->GetKey();
     if (!JS_DefineProperty(cx, obj, PromiseFlatCString(histogramName).get(),
                            snapshot, JSPROP_ENUMERATE)) {
       return false;
     }
     break;
   }
   return true;
 }
 
 bool
-internal_AddonReflector(AddonEntryType *entry, JSContext *cx,
-                        JS::Handle<JSObject*> obj)
+AddonReflector(AddonEntryType *entry, JSContext *cx, JS::Handle<JSObject*> obj)
 {
   const nsACString &addonId = entry->GetKey();
   JS::Rooted<JSObject*> subobj(cx, JS_NewPlainObject(cx));
   if (!subobj) {
     return false;
   }
 
   AddonHistogramMapType *map = entry->mData;
-  if (!(map->ReflectIntoJS(internal_AddonHistogramReflector, cx, subobj)
+  if (!(map->ReflectIntoJS(AddonHistogramReflector, cx, subobj)
         && JS_DefineProperty(cx, obj, PromiseFlatCString(addonId).get(),
                              subobj, JSPROP_ENUMERATE))) {
     return false;
   }
   return true;
 }
 
 } // namespace
 
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
-// PRIVATE: thread-unsafe helpers for the external interface
-
-namespace {
-
-void
-internal_SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID, bool aEnabled)
-{
-  if (!internal_IsHistogramEnumId(aID)) {
-    MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) must be used with an enum id");
-    return;
-  }
+// EXTERNALLY VISIBLE FUNCTIONS
 
-  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);
-    if (NS_SUCCEEDED(rv)) {
-      h->SetRecordingEnabled(aEnabled);
-      return;
-    }
-  }
+namespace TelemetryHistogram {
 
-  MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
-}
-
-void internal_Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample)
-{
-  if (!internal_CanRecordBase()) {
-    return;
-  }
-  Histogram *h;
-  nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h);
-  if (NS_SUCCEEDED(rv)) {
-    internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
-  }
-}
-
-void
-internal_Accumulate(mozilla::Telemetry::ID aID,
-                    const nsCString& aKey, uint32_t aSample)
+void InitializeGlobalState(bool canRecordBase, bool canRecordExtended)
 {
-  if (!gInitDone || !internal_CanRecordBase()) {
-    return;
-  }
-  const HistogramInfo& th = gHistograms[aID];
-  KeyedHistogram* keyed
-     = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
-  MOZ_ASSERT(keyed);
-  keyed->Add(aKey, aSample);
-}
-
-} // namespace
-
-
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-//
-// EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryHistogram::
-
-// 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;
-
-// 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;
 
   // gHistogramMap should have been pre-sized correctly at the
   // declaration point further up in this file.
@@ -1770,361 +1542,343 @@ void TelemetryHistogram::InitializeGloba
       "MAX_EVENT_ID is assumed to be a fixed value in Histograms.json.  If this"
       " was an intentional change, update this assert with its value and update"
       " the n_values for the following in Histograms.json:"
       " STARTUP_MEASUREMENT_ERRORS");
 
   gInitDone = true;
 }
 
-void TelemetryHistogram::DeInitializeGlobalState()
+void DeInitializeGlobalState()
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   gCanRecordBase = false;
   gCanRecordExtended = false;
   gHistogramMap.Clear();
   gKeyedHistograms.Clear();
   gAddonMap.Clear();
   gInitDone = false;
 }
 
 #ifdef DEBUG
-bool TelemetryHistogram::GlobalStateHasBeenInitialized() {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+bool GlobalStateHasBeenInitialized() {
   return gInitDone;
 }
 #endif
 
 bool
-TelemetryHistogram::CanRecordBase() {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  return internal_CanRecordBase();
+CanRecordBase() {
+  return gCanRecordBase;
 }
 
 void
-TelemetryHistogram::SetCanRecordBase(bool b) {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+SetCanRecordBase(bool b) {
   gCanRecordBase = b;
 }
 
 bool
-TelemetryHistogram::CanRecordExtended() {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  return internal_CanRecordExtended();
+CanRecordExtended() {
+  return gCanRecordExtended;
 }
 
 void
-TelemetryHistogram::SetCanRecordExtended(bool b) {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+SetCanRecordExtended(bool b) {
   gCanRecordExtended = b;
 }
 
 
 void
-TelemetryHistogram::InitHistogramRecordingEnabled()
+InitHistogramRecordingEnabled()
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   const size_t length = mozilla::ArrayLength(kRecordingInitiallyDisabledIDs);
   for (size_t i = 0; i < length; i++) {
-    internal_SetHistogramRecordingEnabled(kRecordingInitiallyDisabledIDs[i],
-                                          false);
+    SetHistogramRecordingEnabled(kRecordingInitiallyDisabledIDs[i], false);
   }
 }
 
 void
-TelemetryHistogram::SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID,
-                                                 bool aEnabled)
+SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID, bool aEnabled)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  internal_SetHistogramRecordingEnabled(aID, aEnabled);
+  if (!IsHistogramEnumId(aID)) {
+    MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) must be used with an enum id");
+    return;
+  }
+
+  if (gHistograms[aID].keyed) {
+    const nsDependentCString id(gHistograms[aID].id());
+    KeyedHistogram* keyed = ::GetKeyedHistogramById(id);
+    if (keyed) {
+      keyed->SetRecordingEnabled(aEnabled);
+      return;
+    }
+  } else {
+    Histogram *h;
+    nsresult rv = GetHistogramByEnumId(aID, &h);
+    if (NS_SUCCEEDED(rv)) {
+      h->SetRecordingEnabled(aEnabled);
+      return;
+    }
+  }
+
+  MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
 }
 
 
 nsresult
-TelemetryHistogram::SetHistogramRecordingEnabled(const nsACString &id,
-                                                 bool aEnabled)
+SetHistogramRecordingEnabled(const nsACString &id, bool aEnabled)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   Histogram *h;
-  nsresult rv = internal_GetHistogramByName(id, &h);
+  nsresult rv = GetHistogramByName(id, &h);
   if (NS_SUCCEEDED(rv)) {
     h->SetRecordingEnabled(aEnabled);
     return NS_OK;
   }
 
-  KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
+  KeyedHistogram* keyed = ::GetKeyedHistogramById(id);
   if (keyed) {
     keyed->SetRecordingEnabled(aEnabled);
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
 
 void
-TelemetryHistogram::Accumulate(mozilla::Telemetry::ID aHistogram,
-                               uint32_t aSample)
+Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  internal_Accumulate(aHistogram, aSample);
+  if (!CanRecordBase()) {
+    return;
+  }
+  Histogram *h;
+  nsresult rv = GetHistogramByEnumId(aHistogram, &h);
+  if (NS_SUCCEEDED(rv)) {
+    HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
+  }
 }
 
 void
-TelemetryHistogram::Accumulate(mozilla::Telemetry::ID aID,
-                               const nsCString& aKey, uint32_t aSample)
+Accumulate(mozilla::Telemetry::ID aID, const nsCString& aKey, uint32_t aSample)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  internal_Accumulate(aID, aKey, aSample);
+  if (!gInitDone || !CanRecordBase()) {
+    return;
+  }
+  const HistogramInfo& th = gHistograms[aID];
+  KeyedHistogram* keyed
+     = ::GetKeyedHistogramById(nsDependentCString(th.id()));
+  MOZ_ASSERT(keyed);
+  keyed->Add(aKey, aSample);
+
 }
 
 void
-TelemetryHistogram::Accumulate(const char* name, uint32_t sample)
+Accumulate(const char* name, uint32_t sample)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  if (!internal_CanRecordBase()) {
+  if (!CanRecordBase()) {
     return;
   }
   mozilla::Telemetry::ID id;
-  nsresult rv = internal_GetHistogramEnumId(name, &id);
+  nsresult rv = ::GetHistogramEnumId(name, &id);
   if (NS_FAILED(rv)) {
     return;
   }
 
   Histogram *h;
-  rv = internal_GetHistogramByEnumId(id, &h);
+  rv = GetHistogramByEnumId(id, &h);
   if (NS_SUCCEEDED(rv)) {
-    internal_HistogramAdd(*h, sample, gHistograms[id].dataset);
+    HistogramAdd(*h, sample, gHistograms[id].dataset);
   }
 }
 
 void
-TelemetryHistogram::Accumulate(const char* name,
-                               const nsCString& key, uint32_t sample)
+Accumulate(const char* name, const nsCString& key, uint32_t sample)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  if (!internal_CanRecordBase()) {
+  if (!CanRecordBase()) {
     return;
   }
   mozilla::Telemetry::ID id;
-  nsresult rv = internal_GetHistogramEnumId(name, &id);
+  nsresult rv = ::GetHistogramEnumId(name, &id);
   if (NS_SUCCEEDED(rv)) {
-    internal_Accumulate(id, key, sample);
+    Accumulate(id, key, sample);
   }
 }
 
 void
-TelemetryHistogram::ClearHistogram(mozilla::Telemetry::ID aId)
+ClearHistogram(mozilla::Telemetry::ID aId)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  if (!internal_CanRecordBase()) {
+  if (!TelemetryHistogram::CanRecordBase()) {
     return;
   }
 
   Histogram *h;
-  nsresult rv = internal_GetHistogramByEnumId(aId, &h);
+  nsresult rv = ::GetHistogramByEnumId(aId, &h);
   if (NS_SUCCEEDED(rv) && h) {
-    internal_HistogramClear(*h, false);
+    ::HistogramClear(*h, false);
   }
 }
 
 nsresult
-TelemetryHistogram::GetHistogramById(const nsACString &name, JSContext *cx,
-                                     JS::MutableHandle<JS::Value> ret)
+GetHistogramById(const nsACString &name, JSContext *cx,
+                 JS::MutableHandle<JS::Value> ret)
 {
-  Histogram *h = nullptr;
-  {
-    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    nsresult rv = internal_GetHistogramByName(name, &h);
-    if (NS_FAILED(rv))
-      return rv;
-  }
-  // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnHistogram(h, cx, ret);
+  Histogram *h;
+  nsresult rv = GetHistogramByName(name, &h);
+  if (NS_FAILED(rv))
+    return rv;
+
+  return WrapAndReturnHistogram(h, cx, ret);
 }
 
 nsresult
-TelemetryHistogram::GetKeyedHistogramById(const nsACString &name,
-                                          JSContext *cx,
-                                          JS::MutableHandle<JS::Value> ret)
+GetKeyedHistogramById(const nsACString &name, JSContext *cx,
+                      JS::MutableHandle<JS::Value> ret)
 {
   KeyedHistogram* keyed = nullptr;
-  {
-    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    if (!gKeyedHistograms.Get(name, &keyed)) {
-      return NS_ERROR_FAILURE;
-    }
+  if (!gKeyedHistograms.Get(name, &keyed)) {
+    return NS_ERROR_FAILURE;
   }
-  // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnKeyedHistogram(keyed, cx, ret);
+
+  return WrapAndReturnKeyedHistogram(keyed, cx, ret);
+
 }
 
 const char*
-TelemetryHistogram::GetHistogramName(mozilla::Telemetry::ID id)
+GetHistogramName(mozilla::Telemetry::ID id)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   const HistogramInfo& h = gHistograms[id];
   return h.id();
 }
 
 nsresult
-TelemetryHistogram::NewHistogram(const nsACString &name,
-                                 const nsACString &expiration,
-                                 uint32_t histogramType,
-                                 uint32_t min, uint32_t max,
-                                 uint32_t bucketCount, JSContext *cx,
-                                 uint8_t optArgCount,
-                                 JS::MutableHandle<JS::Value> ret)
+NewHistogram(const nsACString &name, const nsACString &expiration,
+             uint32_t histogramType, uint32_t min, uint32_t max,
+             uint32_t bucketCount, JSContext *cx,
+             uint8_t optArgCount, JS::MutableHandle<JS::Value> ret)
 {
-  Histogram *h = nullptr;
-  {
-    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    if (!internal_IsValidHistogramName(name)) {
-      return NS_ERROR_INVALID_ARG;
-    }
-
-    nsresult rv = internal_HistogramGet(PromiseFlatCString(name).get(),
-                                        PromiseFlatCString(expiration).get(),
-                                        histogramType, min, max, bucketCount,
-                                        optArgCount == 3, &h);
-    if (NS_FAILED(rv))
-      return rv;
-    h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
+  if (!IsValidHistogramName(name)) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnHistogram(h, cx, ret);
+  Histogram *h;
+  nsresult rv = HistogramGet(PromiseFlatCString(name).get(),
+                             PromiseFlatCString(expiration).get(),
+                             histogramType, min, max, bucketCount,
+                             optArgCount == 3, &h);
+  if (NS_FAILED(rv))
+    return rv;
+  h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
+  return WrapAndReturnHistogram(h, cx, ret);
+
 }
 
 nsresult
-TelemetryHistogram::NewKeyedHistogram(const nsACString &name,
-                                      const nsACString &expiration,
-                                      uint32_t histogramType,
-                                      uint32_t min, uint32_t max,
-                                      uint32_t bucketCount, JSContext *cx,
-                                      uint8_t optArgCount,
-                                      JS::MutableHandle<JS::Value> ret)
+NewKeyedHistogram(const nsACString &name, const nsACString &expiration,
+                  uint32_t histogramType, uint32_t min, uint32_t max,
+                  uint32_t bucketCount, JSContext *cx,
+                  uint8_t optArgCount, JS::MutableHandle<JS::Value> ret)
 {
-  KeyedHistogram* keyed = nullptr;
-  {
-    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    if (!internal_IsValidHistogramName(name)) {
-      return NS_ERROR_INVALID_ARG;
-    }
-
-    nsresult rv
-      = internal_CheckHistogramArguments(histogramType, min, max,
-                                         bucketCount, optArgCount == 3);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    keyed = new KeyedHistogram(name, expiration, histogramType,
-                               min, max, bucketCount,
-                               nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN);
-    if (MOZ_UNLIKELY(!gKeyedHistograms.Put(name, keyed, mozilla::fallible))) {
-      delete keyed;
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+  if (!IsValidHistogramName(name)) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnKeyedHistogram(keyed, cx, ret);
+  nsresult rv = CheckHistogramArguments(histogramType, min, max, bucketCount, optArgCount == 3);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  KeyedHistogram* keyed = new KeyedHistogram(name, expiration, histogramType,
+                                             min, max, bucketCount,
+                                             nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN);
+  if (MOZ_UNLIKELY(!gKeyedHistograms.Put(name, keyed, mozilla::fallible))) {
+    delete keyed;
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  return WrapAndReturnKeyedHistogram(keyed, cx, ret);
+
 }
 
 nsresult
-TelemetryHistogram::HistogramFrom(const nsACString &name,
-                                  const nsACString &existing_name,
-                                  JSContext *cx,
-                                  JS::MutableHandle<JS::Value> ret)
+HistogramFrom(const nsACString &name, const nsACString &existing_name,
+              JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
-  Histogram* clone = nullptr;
-  {
-    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    mozilla::Telemetry::ID id;
-    nsresult rv
-      = internal_GetHistogramEnumId(PromiseFlatCString(existing_name).get(),
-                                    &id);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    clone = internal_CloneHistogram(name, id);
-    if (!clone) {
-      return NS_ERROR_FAILURE;
-    }
+   mozilla::Telemetry::ID id;
+  nsresult rv = ::GetHistogramEnumId(PromiseFlatCString(existing_name).get(), &id);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnHistogram(clone, cx, ret);
+  Histogram* clone = CloneHistogram(name, id);
+  if (!clone) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return WrapAndReturnHistogram(clone, cx, ret);
 }
 
 nsresult
-TelemetryHistogram::CreateHistogramSnapshots(JSContext *cx,
-                                             JS::MutableHandle<JS::Value> ret,
-                                             bool subsession,
-                                             bool clearSubsession)
+CreateHistogramSnapshots(JSContext *cx,
+                         JS::MutableHandle<JS::Value> ret,
+                         bool subsession,
+                         bool clearSubsession)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
   if (!root_obj)
     return NS_ERROR_FAILURE;
   ret.setObject(*root_obj);
 
   // Ensure that all the HISTOGRAM_FLAG & HISTOGRAM_COUNT histograms have
   // been created, so that their values are snapshotted.
   for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
     if (gHistograms[i].keyed) {
       continue;
     }
     const uint32_t type = gHistograms[i].histogramType;
     if (type == nsITelemetry::HISTOGRAM_FLAG ||
         type == nsITelemetry::HISTOGRAM_COUNT) {
       Histogram *h;
       mozilla::DebugOnly<nsresult> rv
-         = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h);
+         = GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
 
   StatisticsRecorder::Histograms hs;
   StatisticsRecorder::GetHistograms(&hs);
 
   // 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);
+  IdentifyCorruptHistograms(hs);
 
   // OK, now we can actually reflect things.
   JS::Rooted<JSObject*> hobj(cx);
   for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
     Histogram *h = *it;
-    if (!internal_ShouldReflectHistogram(h) || internal_IsEmpty(h) ||
-        internal_IsExpired(h)) {
+    if (!ShouldReflectHistogram(h) || IsEmpty(h) || IsExpired(h)) {
       continue;
     }
 
     Histogram* original = h;
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
     if (subsession) {
-      h = internal_GetSubsessionHistogram(*h);
+      h = GetSubsessionHistogram(*h);
       if (!h) {
         continue;
       }
     }
 #endif
 
     hobj = JS_NewPlainObject(cx);
     if (!hobj) {
       return NS_ERROR_FAILURE;
     }
-    switch (internal_ReflectHistogramSnapshot(cx, hobj, h)) {
+    switch (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:
@@ -2139,39 +1893,32 @@ TelemetryHistogram::CreateHistogramSnaps
       h->Clear();
     }
 #endif
   }
   return NS_OK;
 }
 
 nsresult
-TelemetryHistogram::RegisteredHistograms(uint32_t aDataset, uint32_t *aCount,
-                                         char*** aHistograms)
+RegisteredHistograms(uint32_t aDataset, uint32_t *aCount,
+                     char*** aHistograms)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  return internal_GetRegisteredHistogramIds(false,
-                                            aDataset, aCount, aHistograms);
+  return GetRegisteredHistogramIds(false, aDataset, aCount, aHistograms);
 }
 
 nsresult
-TelemetryHistogram::RegisteredKeyedHistograms(uint32_t aDataset,
-                                              uint32_t *aCount,
-                                              char*** aHistograms)
+RegisteredKeyedHistograms(uint32_t aDataset, uint32_t *aCount,
+                          char*** aHistograms)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  return internal_GetRegisteredHistogramIds(true,
-                                            aDataset, aCount, aHistograms);
+  return GetRegisteredHistogramIds(true, aDataset, aCount, aHistograms);
 }
 
 nsresult
-TelemetryHistogram::GetKeyedHistogramSnapshots(JSContext *cx,
-                                               JS::MutableHandle<JS::Value> ret)
+GetKeyedHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
   if (!obj) {
     return NS_ERROR_FAILURE;
   }
 
   for (auto iter = gKeyedHistograms.Iter(); !iter.Done(); iter.Next()) {
     JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
     if (!snapshot) {
@@ -2188,24 +1935,20 @@ TelemetryHistogram::GetKeyedHistogramSna
     }
   }
 
   ret.setObject(*obj);
   return NS_OK;
 }
 
 nsresult
-TelemetryHistogram::RegisterAddonHistogram(const nsACString &id,
-                                           const nsACString &name,
-                                           uint32_t histogramType,
-                                           uint32_t min, uint32_t max,
-                                           uint32_t bucketCount,
-                                           uint8_t optArgCount)
+RegisterAddonHistogram(const nsACString &id, const nsACString &name,
+                       uint32_t histogramType, uint32_t min, uint32_t max,
+                       uint32_t bucketCount, uint8_t optArgCount)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   if (histogramType == nsITelemetry::HISTOGRAM_EXPONENTIAL ||
       histogramType == nsITelemetry::HISTOGRAM_LINEAR) {
     if (optArgCount != 3) {
       return NS_ERROR_INVALID_ARG;
     }
 
     // Sanity checks for histogram parameters.
     if (min >= max)
@@ -2248,100 +1991,87 @@ TelemetryHistogram::RegisterAddonHistogr
   info.max = max;
   info.bucketCount = bucketCount;
   info.histogramType = histogramType;
 
   return NS_OK;
 }
 
 nsresult
-TelemetryHistogram::GetAddonHistogram(const nsACString &id,
-                                      const nsACString &name,
-                                      JSContext *cx,
-                                      JS::MutableHandle<JS::Value> ret)
+GetAddonHistogram(const nsACString &id, const nsACString &name,
+                  JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
-  AddonHistogramInfo* info = nullptr;
-  {
-    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-    AddonEntryType *addonEntry = gAddonMap.GetEntry(id);
-    // The given id has not been registered.
-    if (!addonEntry) {
-      return NS_ERROR_INVALID_ARG;
-    }
+  AddonEntryType *addonEntry = gAddonMap.GetEntry(id);
+  // The given id has not been registered.
+  if (!addonEntry) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
-    AddonHistogramMapType *histogramMap = addonEntry->mData;
-    AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name);
-    // The given histogram name has not been registered.
-    if (!histogramEntry) {
-      return NS_ERROR_INVALID_ARG;
-    }
+  AddonHistogramMapType *histogramMap = addonEntry->mData;
+  AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name);
+  // The given histogram name has not been registered.
+  if (!histogramEntry) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
-    info = &histogramEntry->mData;
-    if (!info->h) {
-      nsAutoCString actualName;
-      internal_AddonHistogramName(id, name, actualName);
-      if (!internal_CreateHistogramForAddon(actualName, *info)) {
-        return NS_ERROR_FAILURE;
-      }
+  AddonHistogramInfo &info = histogramEntry->mData;
+  if (!info.h) {
+    nsAutoCString actualName;
+    AddonHistogramName(id, name, actualName);
+    if (!::CreateHistogramForAddon(actualName, info)) {
+      return NS_ERROR_FAILURE;
     }
   }
-
-  // Runs without protection from |gTelemetryHistogramMutex|
-  return internal_WrapAndReturnHistogram(info->h, cx, ret);
+  return WrapAndReturnHistogram(info.h, cx, ret);
 }
 
 nsresult
-TelemetryHistogram::UnregisterAddonHistograms(const nsACString &id)
+UnregisterAddonHistograms(const nsACString &id)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   AddonEntryType *addonEntry = gAddonMap.GetEntry(id);
   if (addonEntry) {
     // Histogram's destructor is private, so this is the best we can do.
     // The histograms the addon created *will* stick around, but they
     // will be deleted if and when the addon registers histograms with
     // the same names.
     delete addonEntry->mData;
     gAddonMap.RemoveEntry(addonEntry);
   }
 
   return NS_OK;
 }
 
 nsresult
-TelemetryHistogram::GetAddonHistogramSnapshots(JSContext *cx,
-                                               JS::MutableHandle<JS::Value> ret)
+GetAddonHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
   if (!obj) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!gAddonMap.ReflectIntoJS(internal_AddonReflector, cx, obj)) {
+  if (!gAddonMap.ReflectIntoJS(AddonReflector, cx, obj)) {
     return NS_ERROR_FAILURE;
   }
   ret.setObject(*obj);
   return NS_OK;
 }
 
 size_t
-TelemetryHistogram::GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf
-                                                      aMallocSizeOf)
+GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   return gAddonMap.ShallowSizeOfExcludingThis(aMallocSizeOf) +
          gHistogramMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
 }
 
 size_t
-TelemetryHistogram::GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf
-                                                     aMallocSizeOf)
+GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
 {
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   StatisticsRecorder::Histograms hs;
   StatisticsRecorder::GetHistograms(&hs);
   size_t n = 0;
   for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
     Histogram *h = *it;
     n += h->SizeOfIncludingThis(aMallocSizeOf);
   }
   return n;
 }
+
+} // namespace TelemetryHistogram
--- a/toolkit/components/telemetry/TelemetryHistogram.h
+++ b/toolkit/components/telemetry/TelemetryHistogram.h
@@ -10,19 +10,16 @@
 
 // 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);
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -88,16 +88,18 @@
 #include "nsPIDOMWindow.h"
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 #include "nsIDocShell.h"
 #include "nsAppShellCID.h"
 #include "mozilla/scache/StartupCache.h"
 #include "gfxPrefs.h"
 
+#include "base/histogram.h"
+
 #include "mozilla/unused.h"
 
 #ifdef XP_WIN
 #include "nsIWinAppHelper.h"
 #include <windows.h>
 #include <intrin.h>
 #include <math.h>
 #include "cairo/cairo-features.h"
@@ -4562,19 +4564,22 @@ void
 XRE_StopLateWriteChecks(void) {
   mozilla::StopLateWriteChecks();
 }
 
 // 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();
+  // A initializer to initialize histogram collection, a chromium
+  // thing used by Telemetry (and effectively a global; it's all static).
+  // Note: purposely leaked
+  base::StatisticsRecorder* statistics_recorder = new base::StatisticsRecorder();
+  MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(statistics_recorder);
+  Unused << statistics_recorder;
 }
 
 int
 XRE_main(int argc, char* argv[], const nsXREAppData* aAppData, uint32_t aFlags)
 {
   XREMain main;
 
   XRE_CreateStatsObject();
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -71,17 +71,17 @@
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/WindowsDllBlocklist.h"
 
 #include "GMPProcessChild.h"
 #include "GMPLoader.h"
 
 #include "GeckoProfiler.h"
 
-#include "mozilla/Telemetry.h"
+ #include "base/histogram.h"
 
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #include "mozilla/sandboxTarget.h"
 #include "mozilla/sandboxing/loggingCallbacks.h"
 #endif
 
 #ifdef MOZ_IPDL_TESTS
 #include "mozilla/_ipdltest/IPDLUnitTests.h"
@@ -313,17 +313,18 @@ XRE_InitChildProcess(int aArgc,
 #endif
 
 #ifdef MOZ_JPROF
   // Call the code to install our handler
   setupProfilingStuff();
 #endif
 
   // This is needed by Telemetry to initialize histogram collection.
-  Telemetry::CreateStatisticsRecorder();
+  UniquePtr<base::StatisticsRecorder> statisticsRecorder =
+    MakeUnique<base::StatisticsRecorder>();
 
 #if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
   // On non-Fennec Gecko, the GMPLoader code resides in plugin-container,
   // and we must forward it through to the GMP code here.
   GMPProcessChild::SetGMPLoader(aChildData->gmpLoader.get());
 #else
   // On Fennec, the GMPLoader's code resides inside XUL (because for the time
   // being GMPLoader relies upon NSPR, which we can't use in plugin-container
@@ -662,17 +663,17 @@ XRE_InitChildProcess(int aArgc,
 
 #if defined(XP_MACOSX)
       // Everybody should be done using shared memory by now.
       mozilla::ipc::SharedMemoryBasic::Shutdown();
 #endif
     }
   }
 
-  Telemetry::DestroyStatisticsRecorder();
+  statisticsRecorder = nullptr;
   profiler_shutdown();
   NS_LogTerm();
   return XRE_DeinitCommandLine();
 }
 
 MessageLoop*
 XRE_GetIOMessageLoop()
 {