LCOV - code coverage report
Current view: top level - toolkit/components/telemetry - TelemetryHistogram.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 440 978 45.0 %
Date: 2017-07-14 16:53:18 Functions: 53 93 57.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "jsapi.h"
       8             : #include "jsfriendapi.h"
       9             : #include "js/GCAPI.h"
      10             : #include "nsString.h"
      11             : #include "nsTHashtable.h"
      12             : #include "nsHashKeys.h"
      13             : #include "nsBaseHashtable.h"
      14             : #include "nsClassHashtable.h"
      15             : #include "nsITelemetry.h"
      16             : 
      17             : #include "mozilla/dom/ToJSValue.h"
      18             : #include "mozilla/gfx/GPUProcessManager.h"
      19             : #include "mozilla/Atomics.h"
      20             : #include "mozilla/StartupTimeline.h"
      21             : #include "mozilla/StaticMutex.h"
      22             : #include "mozilla/Unused.h"
      23             : 
      24             : #include "TelemetryCommon.h"
      25             : #include "TelemetryHistogram.h"
      26             : #include "ipc/TelemetryIPCAccumulator.h"
      27             : 
      28             : #include "base/histogram.h"
      29             : 
      30             : using base::Histogram;
      31             : using base::StatisticsRecorder;
      32             : using base::BooleanHistogram;
      33             : using base::CountHistogram;
      34             : using base::FlagHistogram;
      35             : using base::LinearHistogram;
      36             : using mozilla::StaticMutex;
      37             : using mozilla::StaticMutexAutoLock;
      38             : using mozilla::Telemetry::Accumulation;
      39             : using mozilla::Telemetry::KeyedAccumulation;
      40             : using mozilla::Telemetry::ProcessID;
      41             : using mozilla::Telemetry::Common::LogToBrowserConsole;
      42             : using mozilla::Telemetry::Common::RecordedProcessType;
      43             : 
      44             : namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
      45             : 
      46             : ////////////////////////////////////////////////////////////////////////
      47             : ////////////////////////////////////////////////////////////////////////
      48             : //
      49             : // Naming: there are two kinds of functions in this file:
      50             : //
      51             : // * Functions named internal_*: these can only be reached via an
      52             : //   interface function (TelemetryHistogram::*).  They mostly expect
      53             : //   the interface function to have acquired
      54             : //   |gTelemetryHistogramMutex|, so they do not have to be
      55             : //   thread-safe.  However, those internal_* functions that are
      56             : //   reachable from internal_WrapAndReturnHistogram and
      57             : //   internal_WrapAndReturnKeyedHistogram can sometimes be called
      58             : //   without |gTelemetryHistogramMutex|, and so might be racey.
      59             : //
      60             : // * Functions named TelemetryHistogram::*.  This is the external interface.
      61             : //   Entries and exits to these functions are serialised using
      62             : //   |gTelemetryHistogramMutex|, except for GetKeyedHistogramSnapshots and
      63             : //   CreateHistogramSnapshots.
      64             : //
      65             : // Avoiding races and deadlocks:
      66             : //
      67             : // All functions in the external interface (TelemetryHistogram::*) are
      68             : // serialised using the mutex |gTelemetryHistogramMutex|.  This means
      69             : // that the external interface is thread-safe, and many of the
      70             : // internal_* functions can ignore thread safety.  But it also brings
      71             : // a danger of deadlock if any function in the external interface can
      72             : // get back to that interface.  That is, we will deadlock on any call
      73             : // chain like this
      74             : //
      75             : // TelemetryHistogram::* -> .. any functions .. -> TelemetryHistogram::*
      76             : //
      77             : // To reduce the danger of that happening, observe the following rules:
      78             : //
      79             : // * No function in TelemetryHistogram::* may directly call, nor take the
      80             : //   address of, any other function in TelemetryHistogram::*.
      81             : //
      82             : // * No internal function internal_* may call, nor take the address
      83             : //   of, any function in TelemetryHistogram::*.
      84             : //
      85             : // internal_WrapAndReturnHistogram and
      86             : // internal_WrapAndReturnKeyedHistogram are not protected by
      87             : // |gTelemetryHistogramMutex| because they make calls to the JS
      88             : // engine, but that can in turn call back to Telemetry and hence back
      89             : // to a TelemetryHistogram:: function, in order to report GC and other
      90             : // statistics.  This would lead to deadlock due to attempted double
      91             : // acquisition of |gTelemetryHistogramMutex|, if the internal_* functions
      92             : // were required to be protected by |gTelemetryHistogramMutex|.  To
      93             : // break that cycle, we relax that requirement.  Unfortunately this
      94             : // means that this file is not guaranteed race-free.
      95             : 
      96             : 
      97             : ////////////////////////////////////////////////////////////////////////
      98             : ////////////////////////////////////////////////////////////////////////
      99             : //
     100             : // PRIVATE TYPES
     101             : 
     102             : #define EXPIRED_ID "__expired__"
     103             : #define SUBSESSION_HISTOGRAM_PREFIX "sub#"
     104             : #define KEYED_HISTOGRAM_NAME_SEPARATOR "#"
     105             : #define CONTENT_HISTOGRAM_SUFFIX "#content"
     106             : #define GPU_HISTOGRAM_SUFFIX "#gpu"
     107             : #define EXTENSION_HISTOGRAM_SUFFIX "#extension"
     108             : 
     109             : namespace {
     110             : 
     111             : using mozilla::Telemetry::Common::AutoHashtable;
     112             : using mozilla::Telemetry::Common::IsExpiredVersion;
     113             : using mozilla::Telemetry::Common::CanRecordDataset;
     114             : using mozilla::Telemetry::Common::IsInDataset;
     115             : 
     116             : class KeyedHistogram;
     117             : 
     118             : typedef nsBaseHashtableET<nsDepCharHashKey, mozilla::Telemetry::HistogramID>
     119             :           CharPtrEntryType;
     120             : 
     121             : typedef AutoHashtable<CharPtrEntryType> HistogramMapType;
     122             : 
     123             : typedef nsClassHashtable<nsCStringHashKey, KeyedHistogram>
     124             :           KeyedHistogramMapType;
     125             : 
     126             : // Hardcoded probes
     127             : struct HistogramInfo {
     128             :   uint32_t min;
     129             :   uint32_t max;
     130             :   uint32_t bucketCount;
     131             :   uint32_t histogramType;
     132             :   uint32_t id_offset;
     133             :   uint32_t expiration_offset;
     134             :   uint32_t dataset;
     135             :   uint32_t label_index;
     136             :   uint32_t label_count;
     137             :   RecordedProcessType record_in_processes;
     138             :   bool keyed;
     139             : 
     140             :   const char *id() const;
     141             :   const char *expiration() const;
     142             :   nsresult label_id(const char* label, uint32_t* labelId) const;
     143             : };
     144             : 
     145             : enum reflectStatus {
     146             :   REFLECT_OK,
     147             :   REFLECT_CORRUPT,
     148             :   REFLECT_FAILURE
     149             : };
     150             : 
     151             : typedef StatisticsRecorder::Histograms::iterator HistogramIterator;
     152             : 
     153             : } // namespace
     154             : 
     155             : 
     156             : ////////////////////////////////////////////////////////////////////////
     157             : ////////////////////////////////////////////////////////////////////////
     158             : //
     159             : // PRIVATE STATE, SHARED BY ALL THREADS
     160             : 
     161             : namespace {
     162             : 
     163             : // Set to true once this global state has been initialized
     164             : bool gInitDone = false;
     165             : 
     166             : bool gCanRecordBase = false;
     167             : bool gCanRecordExtended = false;
     168             : 
     169           3 : HistogramMapType gHistogramMap(mozilla::Telemetry::HistogramCount);
     170             : 
     171           3 : KeyedHistogramMapType gKeyedHistograms;
     172             : 
     173             : bool gCorruptHistograms[mozilla::Telemetry::HistogramCount];
     174             : 
     175             : // This is for gHistograms, gHistogramStringTable
     176             : #include "TelemetryHistogramData.inc"
     177             : 
     178             : // The singleton StatisticsRecorder object for this process.
     179             : base::StatisticsRecorder* gStatisticsRecorder = nullptr;
     180             : 
     181             : } // namespace
     182             : 
     183             : 
     184             : ////////////////////////////////////////////////////////////////////////
     185             : ////////////////////////////////////////////////////////////////////////
     186             : //
     187             : // PRIVATE CONSTANTS
     188             : 
     189             : namespace {
     190             : 
     191             : // List of histogram IDs which should have recording disabled initially.
     192             : const mozilla::Telemetry::HistogramID kRecordingInitiallyDisabledIDs[] = {
     193             :   mozilla::Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
     194             : 
     195             :   // The array must not be empty. Leave these item here.
     196             :   mozilla::Telemetry::TELEMETRY_TEST_COUNT_INIT_NO_RECORD,
     197             :   mozilla::Telemetry::TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD
     198             : };
     199             : 
     200             : } // namespace
     201             : 
     202             : 
     203             : ////////////////////////////////////////////////////////////////////////
     204             : ////////////////////////////////////////////////////////////////////////
     205             : //
     206             : // PRIVATE: Misc small helpers
     207             : 
     208             : namespace {
     209             : 
     210             : bool
     211        8440 : internal_CanRecordBase() {
     212        8440 :   return gCanRecordBase;
     213             : }
     214             : 
     215             : bool
     216        3661 : internal_CanRecordExtended() {
     217        3661 :   return gCanRecordExtended;
     218             : }
     219             : 
     220             : bool
     221        4775 : internal_IsHistogramEnumId(mozilla::Telemetry::HistogramID aID)
     222             : {
     223             :   static_assert(((mozilla::Telemetry::HistogramID)-1 > 0), "ID should be unsigned.");
     224        4775 :   return aID < mozilla::Telemetry::HistogramCount;
     225             : }
     226             : 
     227             : // Note: this is completely unrelated to mozilla::IsEmpty.
     228             : bool
     229           0 : internal_IsEmpty(const Histogram *h)
     230             : {
     231           0 :   Histogram::SampleSet ss;
     232           0 :   h->SnapshotSample(&ss);
     233           0 :   return ss.counts(0) == 0 && ss.sum() == 0;
     234             : }
     235             : 
     236             : bool
     237           0 : internal_IsExpired(const Histogram *histogram)
     238             : {
     239           0 :   return histogram->histogram_name() == EXPIRED_ID;
     240             : }
     241             : 
     242             : nsresult
     243           0 : internal_GetRegisteredHistogramIds(bool keyed, uint32_t dataset,
     244             :                                    uint32_t *aCount, char*** aHistograms)
     245             : {
     246           0 :   nsTArray<char*> collection;
     247             : 
     248           0 :   for (const auto & h : gHistograms) {
     249           0 :     if (IsExpiredVersion(h.expiration()) ||
     250           0 :         h.keyed != keyed ||
     251           0 :         !IsInDataset(h.dataset, dataset)) {
     252           0 :       continue;
     253             :     }
     254             : 
     255           0 :     const char* id = h.id();
     256           0 :     const size_t len = strlen(id);
     257           0 :     collection.AppendElement(static_cast<char*>(nsMemory::Clone(id, len+1)));
     258             :   }
     259             : 
     260           0 :   const size_t bytes = collection.Length() * sizeof(char*);
     261           0 :   char** histograms = static_cast<char**>(moz_xmalloc(bytes));
     262           0 :   memcpy(histograms, collection.Elements(), bytes);
     263           0 :   *aHistograms = histograms;
     264           0 :   *aCount = collection.Length();
     265             : 
     266           0 :   return NS_OK;
     267             : }
     268             : 
     269             : const char *
     270       12272 : HistogramInfo::id() const
     271             : {
     272       12272 :   return &gHistogramStringTable[this->id_offset];
     273             : }
     274             : 
     275             : const char *
     276       10066 : HistogramInfo::expiration() const
     277             : {
     278       10066 :   return &gHistogramStringTable[this->expiration_offset];
     279             : }
     280             : 
     281             : nsresult
     282           0 : HistogramInfo::label_id(const char* label, uint32_t* labelId) const
     283             : {
     284           0 :   MOZ_ASSERT(label);
     285           0 :   MOZ_ASSERT(this->histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL);
     286           0 :   if (this->histogramType != nsITelemetry::HISTOGRAM_CATEGORICAL) {
     287           0 :     return NS_ERROR_FAILURE;
     288             :   }
     289             : 
     290           0 :   for (uint32_t i = 0; i < this->label_count; ++i) {
     291             :     // gHistogramLabelTable contains the indices of the label strings in the
     292             :     // gHistogramStringTable.
     293             :     // They are stored in-order and consecutively, from the offset label_index
     294             :     // to (label_index + label_count).
     295           0 :     uint32_t string_offset = gHistogramLabelTable[this->label_index + i];
     296           0 :     const char* const str = &gHistogramStringTable[string_offset];
     297           0 :     if (::strcmp(label, str) == 0) {
     298           0 :       *labelId = i;
     299           0 :       return NS_OK;
     300             :     }
     301             :   }
     302             : 
     303           0 :   return NS_ERROR_FAILURE;
     304             : }
     305             : 
     306             : } // namespace
     307             : 
     308             : 
     309             : ////////////////////////////////////////////////////////////////////////
     310             : ////////////////////////////////////////////////////////////////////////
     311             : //
     312             : // PRIVATE: Histogram Get, Add, Clone, Clear functions
     313             : 
     314             : namespace {
     315             : 
     316             : nsresult
     317        4854 : internal_CheckHistogramArguments(uint32_t histogramType,
     318             :                                  uint32_t min, uint32_t max,
     319             :                                  uint32_t bucketCount, bool haveOptArgs)
     320             : {
     321        4854 :   if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN
     322        3867 :       && histogramType != nsITelemetry::HISTOGRAM_FLAG
     323        3711 :       && histogramType != nsITelemetry::HISTOGRAM_COUNT) {
     324             :     // The min, max & bucketCount arguments are not optional for this type.
     325        3182 :     if (!haveOptArgs)
     326           0 :       return NS_ERROR_ILLEGAL_VALUE;
     327             : 
     328             :     // Sanity checks for histogram parameters.
     329        3182 :     if (min >= max)
     330           0 :       return NS_ERROR_ILLEGAL_VALUE;
     331             : 
     332        3182 :     if (bucketCount <= 2)
     333           0 :       return NS_ERROR_ILLEGAL_VALUE;
     334             : 
     335        3182 :     if (min < 1)
     336           0 :       return NS_ERROR_ILLEGAL_VALUE;
     337             :   }
     338             : 
     339        4854 :   return NS_OK;
     340             : }
     341             : 
     342             : /*
     343             :  * min, max & bucketCount are optional for boolean, flag & count histograms.
     344             :  * haveOptArgs has to be set if the caller provides them.
     345             :  */
     346             : nsresult
     347        4854 : internal_HistogramGet(const char *name, const char *expiration,
     348             :                       uint32_t histogramType, uint32_t min, uint32_t max,
     349             :                       uint32_t bucketCount, bool haveOptArgs,
     350             :                       Histogram **result)
     351             : {
     352        4854 :   nsresult rv = internal_CheckHistogramArguments(histogramType, min, max,
     353        4854 :                                                  bucketCount, haveOptArgs);
     354        4854 :   if (NS_FAILED(rv)) {
     355           0 :     return rv;
     356             :   }
     357             : 
     358        4854 :   if (IsExpiredVersion(expiration)) {
     359         532 :     name = EXPIRED_ID;
     360         532 :     min = 1;
     361         532 :     max = 2;
     362         532 :     bucketCount = 3;
     363         532 :     histogramType = nsITelemetry::HISTOGRAM_LINEAR;
     364             :   }
     365             : 
     366        4854 :   switch (histogramType) {
     367             :   case nsITelemetry::HISTOGRAM_EXPONENTIAL:
     368        1862 :     *result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
     369        1862 :     break;
     370             :   case nsITelemetry::HISTOGRAM_LINEAR:
     371             :   case nsITelemetry::HISTOGRAM_CATEGORICAL:
     372        1567 :     *result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
     373        1567 :     break;
     374             :   case nsITelemetry::HISTOGRAM_BOOLEAN:
     375         930 :     *result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
     376         930 :     break;
     377             :   case nsITelemetry::HISTOGRAM_FLAG:
     378         141 :     *result = FlagHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
     379         141 :     break;
     380             :   case nsITelemetry::HISTOGRAM_COUNT:
     381         354 :     *result = CountHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
     382         354 :     break;
     383             :   default:
     384           0 :     NS_ASSERTION(false, "Invalid histogram type");
     385           0 :     return NS_ERROR_INVALID_ARG;
     386             :   }
     387        4854 :   return NS_OK;
     388             : }
     389             : 
     390             : // Read the process type from the given histogram name. The process type, if
     391             : // one exists, is embedded in a suffix.
     392             : mozilla::Telemetry::ProcessID
     393         183 : GetProcessFromName(const nsACString& aString)
     394             : {
     395         183 :   if (StringEndsWith(aString, NS_LITERAL_CSTRING(CONTENT_HISTOGRAM_SUFFIX))) {
     396           6 :     return ProcessID::Content;
     397             :   }
     398         177 :   if (StringEndsWith(aString, NS_LITERAL_CSTRING(GPU_HISTOGRAM_SUFFIX))) {
     399           0 :     return ProcessID::Gpu;
     400             :   }
     401         177 :   if (StringEndsWith(aString, NS_LITERAL_CSTRING(EXTENSION_HISTOGRAM_SUFFIX))) {
     402           0 :     return ProcessID::Extension;
     403             :   }
     404         177 :   return ProcessID::Parent;
     405             : }
     406             : 
     407             : const char*
     408        5116 : SuffixForProcessType(mozilla::Telemetry::ProcessID aProcessType)
     409             : {
     410        5116 :   switch (aProcessType) {
     411             :     case ProcessID::Parent:
     412        4910 :       return nullptr;
     413             :     case ProcessID::Content:
     414         206 :       return CONTENT_HISTOGRAM_SUFFIX;
     415             :     case ProcessID::Gpu:
     416           0 :       return GPU_HISTOGRAM_SUFFIX;
     417             :     case ProcessID::Extension:
     418           0 :       return EXTENSION_HISTOGRAM_SUFFIX;
     419             :     default:
     420           0 :       MOZ_ASSERT_UNREACHABLE("unknown process type");
     421             :       return nullptr;
     422             :   }
     423             : }
     424             : 
     425             : CharPtrEntryType*
     426         104 : internal_GetHistogramMapEntry(const char* aName)
     427             : {
     428         208 :   nsDependentCString name(aName);
     429         104 :   ProcessID process = GetProcessFromName(name);
     430         104 :   const char* suffix = SuffixForProcessType(process);
     431         104 :   if (!suffix) {
     432         101 :     return gHistogramMap.GetEntry(aName);
     433             :   }
     434             : 
     435           6 :   auto root = Substring(name, 0, name.Length() - strlen(suffix));
     436           3 :   return gHistogramMap.GetEntry(PromiseFlatCString(root).get());
     437             : }
     438             : 
     439             : nsresult
     440         104 : internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::HistogramID *id)
     441             : {
     442         104 :   if (!gInitDone) {
     443           0 :     return NS_ERROR_FAILURE;
     444             :   }
     445             : 
     446         104 :   CharPtrEntryType *entry = internal_GetHistogramMapEntry(name);
     447         104 :   if (!entry) {
     448          10 :     return NS_ERROR_INVALID_ARG;
     449             :   }
     450          94 :   *id = entry->mData;
     451          94 :   return NS_OK;
     452             : }
     453             : 
     454             : // O(1) histogram lookup by numeric id
     455             : nsresult
     456        8125 : internal_GetHistogramByEnumId(mozilla::Telemetry::HistogramID id, Histogram **ret,
     457             :                               ProcessID aProcessType)
     458             : {
     459             :   static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0};
     460             :   static Histogram* knownContentHistograms[mozilla::Telemetry::HistogramCount] = {0};
     461             :   static Histogram* knownGPUHistograms[mozilla::Telemetry::HistogramCount] = {0};
     462             :   static Histogram* knownExtensionHistograms[mozilla::Telemetry::HistogramCount] = {0};
     463             : 
     464        8125 :   Histogram** knownList = nullptr;
     465             : 
     466        8125 :   switch (aProcessType) {
     467             :   case ProcessID::Parent:
     468        7595 :     knownList = knownHistograms;
     469        7595 :     break;
     470             :   case ProcessID::Content:
     471         530 :     knownList = knownContentHistograms;
     472         530 :     break;
     473             :   case ProcessID::Gpu:
     474           0 :     knownList = knownGPUHistograms;
     475           0 :     break;
     476             :   case ProcessID::Extension:
     477           0 :     knownList = knownExtensionHistograms;
     478           0 :     break;
     479             :   default:
     480           0 :     MOZ_ASSERT_UNREACHABLE("unknown process type");
     481             :     return NS_ERROR_FAILURE;
     482             :   }
     483             : 
     484        8125 :   Histogram* h = knownList[id];
     485        8125 :   if (h) {
     486        3290 :     *ret = h;
     487        3290 :     return NS_OK;
     488             :   }
     489             : 
     490        4835 :   const HistogramInfo &p = gHistograms[id];
     491        4835 :   if (p.keyed) {
     492           0 :     return NS_ERROR_FAILURE;
     493             :   }
     494             : 
     495        9670 :   nsAutoCString histogramName;
     496        4835 :   histogramName.Append(p.id());
     497        4835 :   if (const char* suffix = SuffixForProcessType(aProcessType)) {
     498          26 :     histogramName.AppendASCII(suffix);
     499             :   }
     500             : 
     501        4835 :   nsresult rv = internal_HistogramGet(histogramName.get(), p.expiration(),
     502        4835 :                                       p.histogramType, p.min, p.max,
     503        9670 :                                       p.bucketCount, true, &h);
     504        4835 :   if (NS_FAILED(rv))
     505           0 :     return rv;
     506             : 
     507             : #ifdef DEBUG
     508             :   // Check that the C++ Histogram code computes the same ranges as the
     509             :   // Python histogram code.
     510        4835 :   if (!IsExpiredVersion(p.expiration())) {
     511        4303 :     const struct bounds &b = gBucketLowerBoundIndex[id];
     512        4303 :     if (b.length != 0) {
     513        4303 :       MOZ_ASSERT(size_t(b.length) == h->bucket_count(),
     514             :                  "C++/Python bucket # mismatch");
     515      330036 :       for (int i = 0; i < b.length; ++i) {
     516      325733 :         MOZ_ASSERT(gBucketLowerBounds[b.offset + i] == h->ranges(i),
     517             :                    "C++/Python bucket mismatch");
     518             :       }
     519             :     }
     520             :   }
     521             : #endif
     522             : 
     523        4835 :   knownList[id] = h;
     524        4835 :   *ret = h;
     525        4835 :   return NS_OK;
     526             : }
     527             : 
     528             : nsresult
     529           9 : internal_GetHistogramByName(const nsACString &name, Histogram **ret)
     530             : {
     531             :   mozilla::Telemetry::HistogramID id;
     532             :   nsresult rv
     533           9 :     = internal_GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
     534           9 :   if (NS_FAILED(rv)) {
     535           0 :     return rv;
     536             :   }
     537             : 
     538           9 :   ProcessID process = GetProcessFromName(name);
     539           9 :   rv = internal_GetHistogramByEnumId(id, ret, process);
     540           9 :   if (NS_FAILED(rv))
     541           0 :     return rv;
     542             : 
     543           9 :   return NS_OK;
     544             : }
     545             : 
     546             : 
     547             : #if !defined(MOZ_WIDGET_ANDROID)
     548             : 
     549             : /**
     550             :  * This clones a histogram |existing| with the id |existingId| to a
     551             :  * new histogram with the name |newName|.
     552             :  * For simplicity this is limited to registered histograms.
     553             :  */
     554             : Histogram*
     555          15 : internal_CloneHistogram(const nsACString& newName,
     556             :                         mozilla::Telemetry::HistogramID existingId,
     557             :                         Histogram& existing)
     558             : {
     559          15 :   const HistogramInfo &info = gHistograms[existingId];
     560          15 :   Histogram *clone = nullptr;
     561             :   nsresult rv;
     562             : 
     563          75 :   rv = internal_HistogramGet(PromiseFlatCString(newName).get(),
     564             :                              info.expiration(),
     565          30 :                              info.histogramType, existing.declared_min(),
     566          30 :                              existing.declared_max(), existing.bucket_count(),
     567          15 :                              true, &clone);
     568          15 :   if (NS_FAILED(rv)) {
     569           0 :     return nullptr;
     570             :   }
     571             : 
     572          30 :   Histogram::SampleSet ss;
     573          15 :   existing.SnapshotSample(&ss);
     574          15 :   clone->AddSampleSet(ss);
     575             : 
     576          15 :   return clone;
     577             : }
     578             : 
     579             : ProcessID
     580          70 : GetProcessFromName(const std::string& aString)
     581             : {
     582         140 :   nsDependentCString string(aString.c_str(), aString.length());
     583         140 :   return GetProcessFromName(string);
     584             : }
     585             : 
     586             : Histogram*
     587          80 : internal_GetSubsessionHistogram(Histogram& existing)
     588             : {
     589             :   mozilla::Telemetry::HistogramID id;
     590             :   nsresult rv
     591          80 :     = internal_GetHistogramEnumId(existing.histogram_name().c_str(), &id);
     592          80 :   if (NS_FAILED(rv) || gHistograms[id].keyed) {
     593          10 :     return nullptr;
     594             :   }
     595             : 
     596             :   static Histogram* subsession[mozilla::Telemetry::HistogramCount] = {};
     597             :   static Histogram* subsessionContent[mozilla::Telemetry::HistogramCount] = {};
     598             :   static Histogram* subsessionGPU[mozilla::Telemetry::HistogramCount] = {};
     599             :   static Histogram* subsessionExtension[mozilla::Telemetry::HistogramCount] = {};
     600             : 
     601          70 :   Histogram** cache = nullptr;
     602             : 
     603          70 :   ProcessID process = GetProcessFromName(existing.histogram_name());
     604          70 :   switch (process) {
     605             :   case ProcessID::Parent:
     606          67 :     cache = subsession;
     607          67 :     break;
     608             :   case ProcessID::Content:
     609           3 :     cache = subsessionContent;
     610           3 :     break;
     611             :   case ProcessID::Gpu:
     612           0 :     cache = subsessionGPU;
     613           0 :     break;
     614             :   case ProcessID::Extension:
     615           0 :     cache = subsessionExtension;
     616           0 :     break;
     617             :   default:
     618           0 :     MOZ_ASSERT_UNREACHABLE("unknown process type");
     619             :     return nullptr;
     620             :   }
     621             : 
     622          70 :   if (Histogram* cached = cache[id]) {
     623          55 :     return cached;
     624             :   }
     625             : 
     626          15 :   NS_NAMED_LITERAL_CSTRING(prefix, SUBSESSION_HISTOGRAM_PREFIX);
     627          30 :   nsDependentCString existingName(gHistograms[id].id());
     628          15 :   if (StringBeginsWith(existingName, prefix)) {
     629           0 :     return nullptr;
     630             :   }
     631             : 
     632          30 :   nsCString subsessionName(prefix);
     633          15 :   subsessionName.Append(existing.histogram_name().c_str());
     634             : 
     635          15 :   Histogram* clone = internal_CloneHistogram(subsessionName, id, existing);
     636          15 :   cache[id] = clone;
     637          15 :   return clone;
     638             : }
     639             : #endif
     640             : 
     641             : nsresult
     642        2479 : internal_HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset)
     643             : {
     644             :   // Check if we are allowed to record the data.
     645        4958 :   bool canRecordDataset = CanRecordDataset(dataset,
     646        2479 :                                            internal_CanRecordBase(),
     647        4958 :                                            internal_CanRecordExtended());
     648        2479 :   if (!canRecordDataset || !histogram.IsRecordingEnabled()) {
     649        2399 :     return NS_OK;
     650             :   }
     651             : 
     652             : #if !defined(MOZ_WIDGET_ANDROID)
     653          80 :   if (Histogram* subsession = internal_GetSubsessionHistogram(histogram)) {
     654          70 :     subsession->Add(value);
     655             :   }
     656             : #endif
     657             : 
     658             :   // It is safe to add to the histogram now: the subsession histogram was already
     659             :   // cloned from this so we won't add the sample twice.
     660          80 :   histogram.Add(value);
     661             : 
     662          80 :   return NS_OK;
     663             : }
     664             : 
     665             : nsresult
     666           9 : internal_HistogramAdd(Histogram& histogram, int32_t value)
     667             : {
     668           9 :   uint32_t dataset = nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN;
     669             :   // We only really care about the dataset of the histogram if we are not recording
     670             :   // extended telemetry. Otherwise, we always record histogram data.
     671           9 :   if (!internal_CanRecordExtended()) {
     672             :     mozilla::Telemetry::HistogramID id;
     673             :     nsresult rv
     674           9 :       = internal_GetHistogramEnumId(histogram.histogram_name().c_str(), &id);
     675           9 :     if (NS_FAILED(rv)) {
     676             :       // If we can't look up the dataset, it might be because the histogram was added
     677             :       // at runtime. Since we're not recording extended telemetry, bail out.
     678           0 :       return NS_OK;
     679             :     }
     680           9 :     dataset = gHistograms[id].dataset;
     681             :   }
     682             : 
     683           9 :   return internal_HistogramAdd(histogram, value, dataset);
     684             : }
     685             : 
     686             : void
     687           0 : internal_HistogramClear(Histogram& aHistogram, bool onlySubsession)
     688             : {
     689           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     690           0 :   if (!XRE_IsParentProcess()) {
     691           0 :     return;
     692             :   }
     693           0 :   if (!onlySubsession) {
     694           0 :     aHistogram.Clear();
     695             :   }
     696             : 
     697             : #if !defined(MOZ_WIDGET_ANDROID)
     698           0 :   if (Histogram* subsession = internal_GetSubsessionHistogram(aHistogram)) {
     699           0 :     subsession->Clear();
     700             :   }
     701             : #endif
     702             : }
     703             : 
     704             : } // namespace
     705             : 
     706             : 
     707             : ////////////////////////////////////////////////////////////////////////
     708             : ////////////////////////////////////////////////////////////////////////
     709             : //
     710             : // PRIVATE: Histogram corruption helpers
     711             : 
     712             : namespace {
     713             : 
     714             : void internal_Accumulate(mozilla::Telemetry::HistogramID aHistogram, uint32_t aSample);
     715             : 
     716             : void
     717           0 : internal_IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs)
     718             : {
     719           0 :   for (auto h : hs) {
     720             :     mozilla::Telemetry::HistogramID id;
     721           0 :     nsresult rv = internal_GetHistogramEnumId(h->histogram_name().c_str(), &id);
     722             :     // This histogram isn't a static histogram, just ignore it.
     723           0 :     if (NS_FAILED(rv)) {
     724           0 :       continue;
     725             :     }
     726             : 
     727           0 :     if (gCorruptHistograms[id]) {
     728           0 :       continue;
     729             :     }
     730             : 
     731           0 :     Histogram::SampleSet ss;
     732           0 :     h->SnapshotSample(&ss);
     733             : 
     734           0 :     Histogram::Inconsistencies check = h->FindCorruption(ss);
     735           0 :     bool corrupt = (check != Histogram::NO_INCONSISTENCIES);
     736             : 
     737           0 :     if (corrupt) {
     738           0 :       mozilla::Telemetry::HistogramID corruptID = mozilla::Telemetry::HistogramCount;
     739           0 :       if (check & Histogram::RANGE_CHECKSUM_ERROR) {
     740           0 :         corruptID = mozilla::Telemetry::RANGE_CHECKSUM_ERRORS;
     741           0 :       } else if (check & Histogram::BUCKET_ORDER_ERROR) {
     742           0 :         corruptID = mozilla::Telemetry::BUCKET_ORDER_ERRORS;
     743           0 :       } else if (check & Histogram::COUNT_HIGH_ERROR) {
     744           0 :         corruptID = mozilla::Telemetry::TOTAL_COUNT_HIGH_ERRORS;
     745           0 :       } else if (check & Histogram::COUNT_LOW_ERROR) {
     746           0 :         corruptID = mozilla::Telemetry::TOTAL_COUNT_LOW_ERRORS;
     747             :       }
     748           0 :       internal_Accumulate(corruptID, 1);
     749             :     }
     750             : 
     751           0 :     gCorruptHistograms[id] = corrupt;
     752             :   }
     753           0 : }
     754             : 
     755             : } // namespace
     756             : 
     757             : 
     758             : ////////////////////////////////////////////////////////////////////////
     759             : ////////////////////////////////////////////////////////////////////////
     760             : //
     761             : // PRIVATE: Histogram reflection helpers
     762             : 
     763             : namespace {
     764             : 
     765             : bool
     766           0 : internal_FillRanges(JSContext *cx, JS::Handle<JSObject*> array, Histogram *h)
     767             : {
     768           0 :   JS::Rooted<JS::Value> range(cx);
     769           0 :   for (size_t i = 0; i < h->bucket_count(); i++) {
     770           0 :     range.setInt32(h->ranges(i));
     771           0 :     if (!JS_DefineElement(cx, array, i, range, JSPROP_ENUMERATE))
     772           0 :       return false;
     773             :   }
     774           0 :   return true;
     775             : }
     776             : 
     777             : enum reflectStatus
     778           0 : internal_ReflectHistogramAndSamples(JSContext *cx,
     779             :                                     JS::Handle<JSObject*> obj, Histogram *h,
     780             :                                     const Histogram::SampleSet &ss)
     781             : {
     782             :   // We don't want to reflect corrupt histograms.
     783           0 :   if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
     784           0 :     return REFLECT_CORRUPT;
     785             :   }
     786             : 
     787           0 :   if (!(JS_DefineProperty(cx, obj, "min",
     788             :                           h->declared_min(), JSPROP_ENUMERATE)
     789           0 :         && JS_DefineProperty(cx, obj, "max",
     790             :                              h->declared_max(), JSPROP_ENUMERATE)
     791           0 :         && JS_DefineProperty(cx, obj, "histogram_type",
     792           0 :                              h->histogram_type(), JSPROP_ENUMERATE)
     793           0 :         && JS_DefineProperty(cx, obj, "sum",
     794           0 :                              double(ss.sum()), JSPROP_ENUMERATE))) {
     795           0 :     return REFLECT_FAILURE;
     796             :   }
     797             : 
     798           0 :   const size_t count = h->bucket_count();
     799           0 :   JS::Rooted<JSObject*> rarray(cx, JS_NewArrayObject(cx, count));
     800           0 :   if (!rarray) {
     801           0 :     return REFLECT_FAILURE;
     802             :   }
     803           0 :   if (!(internal_FillRanges(cx, rarray, h)
     804           0 :         && JS_DefineProperty(cx, obj, "ranges", rarray, JSPROP_ENUMERATE))) {
     805           0 :     return REFLECT_FAILURE;
     806             :   }
     807             : 
     808           0 :   JS::Rooted<JSObject*> counts_array(cx, JS_NewArrayObject(cx, count));
     809           0 :   if (!counts_array) {
     810           0 :     return REFLECT_FAILURE;
     811             :   }
     812           0 :   if (!JS_DefineProperty(cx, obj, "counts", counts_array, JSPROP_ENUMERATE)) {
     813           0 :     return REFLECT_FAILURE;
     814             :   }
     815           0 :   for (size_t i = 0; i < count; i++) {
     816           0 :     if (!JS_DefineElement(cx, counts_array, i,
     817             :                           ss.counts(i), JSPROP_ENUMERATE)) {
     818           0 :       return REFLECT_FAILURE;
     819             :     }
     820             :   }
     821             : 
     822           0 :   return REFLECT_OK;
     823             : }
     824             : 
     825             : enum reflectStatus
     826           0 : internal_ReflectHistogramSnapshot(JSContext *cx,
     827             :                                   JS::Handle<JSObject*> obj, Histogram *h)
     828             : {
     829           0 :   Histogram::SampleSet ss;
     830           0 :   h->SnapshotSample(&ss);
     831           0 :   return internal_ReflectHistogramAndSamples(cx, obj, h, ss);
     832             : }
     833             : 
     834             : bool
     835           0 : internal_ShouldReflectHistogram(Histogram *h)
     836             : {
     837           0 :   const char *name = h->histogram_name().c_str();
     838             :   mozilla::Telemetry::HistogramID id;
     839           0 :   nsresult rv = internal_GetHistogramEnumId(name, &id);
     840           0 :   if (NS_FAILED(rv)) {
     841             :     // GetHistogramEnumId generally should not fail.  But a lookup
     842             :     // failure shouldn't prevent us from reflecting histograms into JS.
     843             :     //
     844             :     // However, these two histograms are created by Histogram itself for
     845             :     // tracking corruption.  We have our own histograms for that, so
     846             :     // ignore these two.
     847           0 :     if (strcmp(name, "Histogram.InconsistentCountHigh") == 0
     848           0 :         || strcmp(name, "Histogram.InconsistentCountLow") == 0) {
     849           0 :       return false;
     850             :     }
     851           0 :     return true;
     852             :   }
     853           0 :   return !gCorruptHistograms[id];
     854             : }
     855             : 
     856             : } // namespace
     857             : 
     858             : 
     859             : ////////////////////////////////////////////////////////////////////////
     860             : ////////////////////////////////////////////////////////////////////////
     861             : //
     862             : // PRIVATE: class KeyedHistogram
     863             : 
     864             : namespace {
     865             : 
     866           0 : class KeyedHistogram {
     867             : public:
     868             :   KeyedHistogram(const nsACString &name, const nsACString &expiration,
     869             :                  uint32_t histogramType, uint32_t min, uint32_t max,
     870             :                  uint32_t bucketCount, uint32_t dataset);
     871             :   nsresult GetHistogram(const nsCString& name, Histogram** histogram, bool subsession);
     872             :   Histogram* GetHistogram(const nsCString& name, bool subsession);
     873           6 :   uint32_t GetHistogramType() const { return mHistogramType; }
     874             :   nsresult GetJSKeys(JSContext* cx, JS::CallArgs& args);
     875             :   nsresult GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
     876             :                          bool subsession, bool clearSubsession);
     877             : 
     878         384 :   void SetRecordingEnabled(bool aEnabled) { mRecordingEnabled = aEnabled; };
     879         300 :   bool IsRecordingEnabled() const { return mRecordingEnabled; };
     880             : 
     881             :   nsresult Add(const nsCString& key, uint32_t aSample);
     882             :   void Clear(bool subsession);
     883             : 
     884             :   nsresult GetEnumId(mozilla::Telemetry::HistogramID& id);
     885             : 
     886             : private:
     887             :   typedef nsBaseHashtableET<nsCStringHashKey, Histogram*> KeyedHistogramEntry;
     888             :   typedef AutoHashtable<KeyedHistogramEntry> KeyedHistogramMapType;
     889             :   KeyedHistogramMapType mHistogramMap;
     890             : #if !defined(MOZ_WIDGET_ANDROID)
     891             :   KeyedHistogramMapType mSubsessionMap;
     892             : #endif
     893             : 
     894             :   static bool ReflectKeyedHistogram(KeyedHistogramEntry* entry,
     895             :                                     JSContext* cx,
     896             :                                     JS::Handle<JSObject*> obj);
     897             : 
     898             :   const nsCString mName;
     899             :   const nsCString mExpiration;
     900             :   const uint32_t mHistogramType;
     901             :   const uint32_t mMin;
     902             :   const uint32_t mMax;
     903             :   const uint32_t mBucketCount;
     904             :   const uint32_t mDataset;
     905             :   mozilla::Atomic<bool, mozilla::Relaxed> mRecordingEnabled;
     906             : };
     907             : 
     908         762 : KeyedHistogram::KeyedHistogram(const nsACString &name,
     909             :                                const nsACString &expiration,
     910             :                                uint32_t histogramType,
     911             :                                uint32_t min, uint32_t max,
     912         762 :                                uint32_t bucketCount, uint32_t dataset)
     913             :   : mHistogramMap()
     914             : #if !defined(MOZ_WIDGET_ANDROID)
     915             :   , mSubsessionMap()
     916             : #endif
     917             :   , mName(name)
     918             :   , mExpiration(expiration)
     919             :   , mHistogramType(histogramType)
     920             :   , mMin(min)
     921             :   , mMax(max)
     922             :   , mBucketCount(bucketCount)
     923             :   , mDataset(dataset)
     924         762 :   , mRecordingEnabled(true)
     925             : {
     926         762 : }
     927             : 
     928             : nsresult
     929          12 : KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram,
     930             :                              bool subsession)
     931             : {
     932             : #if !defined(MOZ_WIDGET_ANDROID)
     933          12 :   KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap;
     934             : #else
     935             :   KeyedHistogramMapType& map = mHistogramMap;
     936             : #endif
     937          12 :   KeyedHistogramEntry* entry = map.GetEntry(key);
     938          12 :   if (entry) {
     939           8 :     *histogram = entry->mData;
     940           8 :     return NS_OK;
     941             :   }
     942             : 
     943           8 :   nsCString histogramName;
     944             : #if !defined(MOZ_WIDGET_ANDROID)
     945           4 :   if (subsession) {
     946           2 :     histogramName.AppendLiteral(SUBSESSION_HISTOGRAM_PREFIX);
     947             :   }
     948             : #endif
     949           4 :   histogramName.Append(mName);
     950           4 :   histogramName.AppendLiteral(KEYED_HISTOGRAM_NAME_SEPARATOR);
     951           4 :   histogramName.Append(key);
     952             : 
     953             :   Histogram* h;
     954           4 :   nsresult rv = internal_HistogramGet(histogramName.get(), mExpiration.get(),
     955           4 :                                       mHistogramType, mMin, mMax, mBucketCount,
     956           4 :                                       true, &h);
     957           4 :   if (NS_FAILED(rv)) {
     958           0 :     return rv;
     959             :   }
     960             : 
     961           4 :   h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
     962           4 :   *histogram = h;
     963             : 
     964           4 :   entry = map.PutEntry(key);
     965           4 :   if (MOZ_UNLIKELY(!entry)) {
     966           0 :     return NS_ERROR_OUT_OF_MEMORY;
     967             :   }
     968             : 
     969           4 :   entry->mData = h;
     970           4 :   return NS_OK;
     971             : }
     972             : 
     973             : Histogram*
     974          12 : KeyedHistogram::GetHistogram(const nsCString& key, bool subsession)
     975             : {
     976          12 :   Histogram* h = nullptr;
     977          12 :   if (NS_FAILED(GetHistogram(key, &h, subsession))) {
     978           0 :     return nullptr;
     979             :   }
     980          12 :   return h;
     981             : }
     982             : 
     983             : nsresult
     984        1173 : KeyedHistogram::Add(const nsCString& key, uint32_t sample)
     985             : {
     986        2346 :   bool canRecordDataset = CanRecordDataset(mDataset,
     987        1173 :                                            internal_CanRecordBase(),
     988        2346 :                                            internal_CanRecordExtended());
     989        1173 :   if (!canRecordDataset || !IsRecordingEnabled()) {
     990        1167 :     return NS_OK;
     991             :   }
     992             : 
     993           6 :   Histogram* histogram = GetHistogram(key, false);
     994           6 :   MOZ_ASSERT(histogram);
     995           6 :   if (!histogram) {
     996           0 :     return NS_ERROR_FAILURE;
     997             :   }
     998             : #if !defined(MOZ_WIDGET_ANDROID)
     999           6 :   Histogram* subsession = GetHistogram(key, true);
    1000           6 :   MOZ_ASSERT(subsession);
    1001           6 :   if (!subsession) {
    1002           0 :     return NS_ERROR_FAILURE;
    1003             :   }
    1004             : #endif
    1005             : 
    1006           6 :   histogram->Add(sample);
    1007             : #if !defined(MOZ_WIDGET_ANDROID)
    1008           6 :   subsession->Add(sample);
    1009             : #endif
    1010           6 :   return NS_OK;
    1011             : }
    1012             : 
    1013             : void
    1014           0 : KeyedHistogram::Clear(bool onlySubsession)
    1015             : {
    1016           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    1017           0 :   if (!XRE_IsParentProcess()) {
    1018           0 :     return;
    1019             :   }
    1020             : #if !defined(MOZ_WIDGET_ANDROID)
    1021           0 :   for (auto iter = mSubsessionMap.Iter(); !iter.Done(); iter.Next()) {
    1022           0 :     iter.Get()->mData->Clear();
    1023             :   }
    1024           0 :   mSubsessionMap.Clear();
    1025           0 :   if (onlySubsession) {
    1026           0 :     return;
    1027             :   }
    1028             : #endif
    1029             : 
    1030           0 :   for (auto iter = mHistogramMap.Iter(); !iter.Done(); iter.Next()) {
    1031           0 :     iter.Get()->mData->Clear();
    1032             :   }
    1033           0 :   mHistogramMap.Clear();
    1034             : }
    1035             : 
    1036             : nsresult
    1037           0 : KeyedHistogram::GetJSKeys(JSContext* cx, JS::CallArgs& args)
    1038             : {
    1039           0 :   JS::AutoValueVector keys(cx);
    1040           0 :   if (!keys.reserve(mHistogramMap.Count())) {
    1041           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1042             :   }
    1043             : 
    1044           0 :   for (auto iter = mHistogramMap.Iter(); !iter.Done(); iter.Next()) {
    1045           0 :     JS::RootedValue jsKey(cx);
    1046           0 :     const NS_ConvertUTF8toUTF16 key(iter.Get()->GetKey());
    1047           0 :     jsKey.setString(JS_NewUCStringCopyN(cx, key.Data(), key.Length()));
    1048           0 :     if (!keys.append(jsKey)) {
    1049           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1050             :     }
    1051             :   }
    1052             : 
    1053           0 :   JS::RootedObject jsKeys(cx, JS_NewArrayObject(cx, keys));
    1054           0 :   if (!jsKeys) {
    1055           0 :     return NS_ERROR_FAILURE;
    1056             :   }
    1057             : 
    1058           0 :   args.rval().setObject(*jsKeys);
    1059           0 :   return NS_OK;
    1060             : }
    1061             : 
    1062             : bool
    1063           0 : KeyedHistogram::ReflectKeyedHistogram(KeyedHistogramEntry* entry,
    1064             :                                       JSContext* cx, JS::Handle<JSObject*> obj)
    1065             : {
    1066           0 :   JS::RootedObject histogramSnapshot(cx, JS_NewPlainObject(cx));
    1067           0 :   if (!histogramSnapshot) {
    1068           0 :     return false;
    1069             :   }
    1070             : 
    1071           0 :   if (internal_ReflectHistogramSnapshot(cx, histogramSnapshot,
    1072             :                                         entry->mData) != REFLECT_OK) {
    1073           0 :     return false;
    1074             :   }
    1075             : 
    1076           0 :   const NS_ConvertUTF8toUTF16 key(entry->GetKey());
    1077           0 :   if (!JS_DefineUCProperty(cx, obj, key.Data(), key.Length(),
    1078             :                            histogramSnapshot, JSPROP_ENUMERATE)) {
    1079           0 :     return false;
    1080             :   }
    1081             : 
    1082           0 :   return true;
    1083             : }
    1084             : 
    1085             : nsresult
    1086           0 : KeyedHistogram::GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
    1087             :                               bool subsession, bool clearSubsession)
    1088             : {
    1089             : #if !defined(MOZ_WIDGET_ANDROID)
    1090           0 :   KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap;
    1091             : #else
    1092             :   KeyedHistogramMapType& map = mHistogramMap;
    1093             : #endif
    1094           0 :   if (!map.ReflectIntoJS(&KeyedHistogram::ReflectKeyedHistogram, cx, obj)) {
    1095           0 :     return NS_ERROR_FAILURE;
    1096             :   }
    1097             : 
    1098             : #if !defined(MOZ_WIDGET_ANDROID)
    1099           0 :   if (subsession && clearSubsession) {
    1100           0 :     Clear(true);
    1101             :   }
    1102             : #endif
    1103             : 
    1104           0 :   return NS_OK;
    1105             : }
    1106             : 
    1107             : nsresult
    1108           6 : KeyedHistogram::GetEnumId(mozilla::Telemetry::HistogramID& id)
    1109             : {
    1110           6 :   return internal_GetHistogramEnumId(mName.get(), &id);
    1111             : }
    1112             : 
    1113             : } // namespace
    1114             : 
    1115             : 
    1116             : ////////////////////////////////////////////////////////////////////////
    1117             : ////////////////////////////////////////////////////////////////////////
    1118             : //
    1119             : // PRIVATE: KeyedHistogram helpers
    1120             : 
    1121             : namespace {
    1122             : 
    1123             : KeyedHistogram*
    1124        1851 : internal_GetKeyedHistogramById(const nsACString &name)
    1125             : {
    1126        1851 :   if (!gInitDone) {
    1127           0 :     return nullptr;
    1128             :   }
    1129             : 
    1130        1851 :   KeyedHistogram* keyed = nullptr;
    1131        1851 :   gKeyedHistograms.Get(name, &keyed);
    1132        1851 :   return keyed;
    1133             : }
    1134             : 
    1135             : } // namespace
    1136             : 
    1137             : 
    1138             : ////////////////////////////////////////////////////////////////////////
    1139             : ////////////////////////////////////////////////////////////////////////
    1140             : //
    1141             : // PRIVATE: thread-unsafe helpers for the external interface
    1142             : 
    1143             : // This is a StaticMutex rather than a plain Mutex (1) so that
    1144             : // it gets initialised in a thread-safe manner the first time
    1145             : // it is used, and (2) because it is never de-initialised, and
    1146             : // a normal Mutex would show up as a leak in BloatView.  StaticMutex
    1147             : // also has the "OffTheBooks" property, so it won't show as a leak
    1148             : // in BloatView.
    1149           3 : static StaticMutex gTelemetryHistogramMutex;
    1150             : 
    1151             : namespace {
    1152             : 
    1153             : void
    1154        5199 : internal_SetHistogramRecordingEnabled(mozilla::Telemetry::HistogramID aID, bool aEnabled)
    1155             : {
    1156        5199 :   if (gHistograms[aID].keyed) {
    1157         384 :     const nsDependentCString id(gHistograms[aID].id());
    1158         384 :     KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
    1159         384 :     if (keyed) {
    1160         384 :       keyed->SetRecordingEnabled(aEnabled);
    1161         384 :       return;
    1162             :     }
    1163             :   } else {
    1164             :     Histogram *h;
    1165        4815 :     nsresult rv = internal_GetHistogramByEnumId(aID, &h, ProcessID::Parent);
    1166        4815 :     if (NS_SUCCEEDED(rv)) {
    1167        4815 :       h->SetRecordingEnabled(aEnabled);
    1168        4815 :       return;
    1169             :     }
    1170             :   }
    1171             : 
    1172           0 :   MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
    1173             : }
    1174             : 
    1175             : bool
    1176        2771 : internal_RemoteAccumulate(mozilla::Telemetry::HistogramID aId, uint32_t aSample)
    1177             : {
    1178        2771 :   if (XRE_IsParentProcess()) {
    1179        1940 :     return false;
    1180             :   }
    1181             :   Histogram *h;
    1182         831 :   nsresult rv = internal_GetHistogramByEnumId(aId, &h, ProcessID::Parent);
    1183         831 :   if (NS_SUCCEEDED(rv) && !h->IsRecordingEnabled()) {
    1184           3 :     return true;
    1185             :   }
    1186         828 :   TelemetryIPCAccumulator::AccumulateChildHistogram(aId, aSample);
    1187         828 :   return true;
    1188             : }
    1189             : 
    1190             : bool
    1191        1290 : internal_RemoteAccumulate(mozilla::Telemetry::HistogramID aId,
    1192             :                     const nsCString& aKey, uint32_t aSample)
    1193             : {
    1194        1290 :   if (XRE_IsParentProcess()) {
    1195         996 :     return false;
    1196             :   }
    1197         294 :   const HistogramInfo& th = gHistograms[aId];
    1198             :   KeyedHistogram* keyed
    1199         294 :      = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
    1200         294 :   MOZ_ASSERT(keyed);
    1201         294 :   if (!keyed->IsRecordingEnabled()) {
    1202           0 :     return false;
    1203             :   }
    1204         294 :   TelemetryIPCAccumulator::AccumulateChildKeyedHistogram(aId, aKey, aSample);
    1205         294 :   return true;
    1206             : }
    1207             : 
    1208        2784 : void internal_Accumulate(mozilla::Telemetry::HistogramID aHistogram, uint32_t aSample)
    1209             : {
    1210        5555 :   if (!internal_CanRecordBase() ||
    1211        2771 :       internal_RemoteAccumulate(aHistogram, aSample)) {
    1212         844 :     return;
    1213             :   }
    1214             :   Histogram *h;
    1215        1940 :   nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h, ProcessID::Parent);
    1216        1940 :   if (NS_SUCCEEDED(rv)) {
    1217        1940 :     internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
    1218             :   }
    1219             : }
    1220             : 
    1221             : void
    1222        1284 : internal_Accumulate(mozilla::Telemetry::HistogramID aID,
    1223             :                     const nsCString& aKey, uint32_t aSample)
    1224             : {
    1225        2568 :   if (!gInitDone || !internal_CanRecordBase() ||
    1226        1284 :       internal_RemoteAccumulate(aID, aKey, aSample)) {
    1227         288 :     return;
    1228             :   }
    1229         996 :   const HistogramInfo& th = gHistograms[aID];
    1230             :   KeyedHistogram* keyed
    1231         996 :      = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
    1232         996 :   MOZ_ASSERT(keyed);
    1233         996 :   keyed->Add(aKey, aSample);
    1234             : }
    1235             : 
    1236             : void
    1237           9 : internal_Accumulate(Histogram& aHistogram, uint32_t aSample)
    1238             : {
    1239           9 :   if (XRE_IsParentProcess()) {
    1240           9 :     internal_HistogramAdd(aHistogram, aSample);
    1241           9 :     return;
    1242             :   }
    1243             : 
    1244             :   mozilla::Telemetry::HistogramID id;
    1245           0 :   nsresult rv = internal_GetHistogramEnumId(aHistogram.histogram_name().c_str(), &id);
    1246           0 :   if (NS_SUCCEEDED(rv)) {
    1247           0 :     internal_RemoteAccumulate(id, aSample);
    1248             :   }
    1249             : }
    1250             : 
    1251             : void
    1252           6 : internal_Accumulate(KeyedHistogram& aKeyed,
    1253             :                     const nsCString& aKey, uint32_t aSample)
    1254             : {
    1255           6 :   if (XRE_IsParentProcess()) {
    1256           0 :     aKeyed.Add(aKey, aSample);
    1257           0 :     return;
    1258             :   }
    1259             : 
    1260             :   mozilla::Telemetry::HistogramID id;
    1261           6 :   if (NS_SUCCEEDED(aKeyed.GetEnumId(id))) {
    1262           6 :     internal_RemoteAccumulate(id, aKey, aSample);
    1263             :   }
    1264             : }
    1265             : 
    1266             : void
    1267         530 : internal_AccumulateChild(ProcessID aProcessType, mozilla::Telemetry::HistogramID aId, uint32_t aSample)
    1268             : {
    1269         530 :   if (!internal_CanRecordBase()) {
    1270           0 :     return;
    1271             :   }
    1272             :   Histogram* h;
    1273         530 :   nsresult rv = internal_GetHistogramByEnumId(aId, &h, aProcessType);
    1274         530 :   if (NS_SUCCEEDED(rv)) {
    1275         530 :     internal_HistogramAdd(*h, aSample, gHistograms[aId].dataset);
    1276             :   } else {
    1277           0 :     NS_WARNING("NS_FAILED GetHistogramByEnumId for CHILD");
    1278             :   }
    1279             : }
    1280             : 
    1281             : void
    1282         177 : internal_AccumulateChildKeyed(ProcessID aProcessType, mozilla::Telemetry::HistogramID aId,
    1283             :                               const nsCString& aKey, uint32_t aSample)
    1284             : {
    1285         177 :   if (!gInitDone || !internal_CanRecordBase()) {
    1286           0 :     return;
    1287             :   }
    1288             : 
    1289         177 :   const char* suffix = SuffixForProcessType(aProcessType);
    1290         177 :   if (!suffix) {
    1291           0 :     MOZ_ASSERT_UNREACHABLE("suffix should not be null");
    1292             :     return;
    1293             :   }
    1294             : 
    1295         177 :   const HistogramInfo& th = gHistograms[aId];
    1296             : 
    1297         354 :   nsAutoCString id;
    1298         177 :   id.Append(th.id());
    1299         177 :   id.AppendASCII(suffix);
    1300             : 
    1301         177 :   KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
    1302         177 :   MOZ_ASSERT(keyed);
    1303         177 :   keyed->Add(aKey, aSample);
    1304             : }
    1305             : 
    1306             : } // namespace
    1307             : 
    1308             : 
    1309             : ////////////////////////////////////////////////////////////////////////
    1310             : ////////////////////////////////////////////////////////////////////////
    1311             : //
    1312             : // PRIVATE: JSHistogram_* functions
    1313             : 
    1314             : // NOTE: the functions in this section:
    1315             : //
    1316             : //   internal_JSHistogram_Add
    1317             : //   internal_JSHistogram_Snapshot
    1318             : //   internal_JSHistogram_Clear
    1319             : //   internal_WrapAndReturnHistogram
    1320             : //
    1321             : // all run without protection from |gTelemetryHistogramMutex|.  If they
    1322             : // held |gTelemetryHistogramMutex|, there would be the possibility of
    1323             : // deadlock because the JS_ calls that they make may call back into the
    1324             : // TelemetryHistogram interface, hence trying to re-acquire the mutex.
    1325             : //
    1326             : // This means that these functions potentially race against threads, but
    1327             : // that seems preferable to risking deadlock.
    1328             : 
    1329             : namespace {
    1330             : 
    1331             : static const JSClass sJSHistogramClass = {
    1332             :   "JSHistogram",  /* name */
    1333             :   JSCLASS_HAS_PRIVATE  /* flags */
    1334             : };
    1335             : 
    1336             : bool
    1337           9 : internal_JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
    1338             : {
    1339           9 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
    1340           9 :   MOZ_ASSERT(obj);
    1341          18 :   if (!obj ||
    1342           9 :       JS_GetClass(obj) != &sJSHistogramClass) {
    1343           0 :     return false;
    1344             :   }
    1345             : 
    1346           9 :   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
    1347           9 :   MOZ_ASSERT(h);
    1348           9 :   Histogram::ClassType type = h->histogram_type();
    1349             : 
    1350           9 :   JS::CallArgs args = CallArgsFromVp(argc, vp);
    1351             :   // This function should always return |undefined| and never fail but
    1352             :   // rather report failures using the console.
    1353           9 :   args.rval().setUndefined();
    1354             : 
    1355           9 :   if (!internal_CanRecordBase()) {
    1356           0 :     return true;
    1357             :   }
    1358             : 
    1359           9 :   uint32_t value = 0;
    1360             :   mozilla::Telemetry::HistogramID id;
    1361           9 :   if ((type == base::CountHistogram::COUNT_HISTOGRAM) && (args.length() == 0)) {
    1362             :     // If we don't have an argument for the count histogram, assume an increment of 1.
    1363             :     // Otherwise, make sure to run some sanity checks on the argument.
    1364           0 :     value = 1;
    1365          27 :   } else if (type == base::LinearHistogram::LINEAR_HISTOGRAM &&
    1366          15 :       (args.length() > 0) && args[0].isString() &&
    1367           9 :       NS_SUCCEEDED(internal_GetHistogramEnumId(h->histogram_name().c_str(), &id)) &&
    1368           0 :       gHistograms[id].histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL) {
    1369             :     // For categorical histograms we allow passing a string argument that specifies the label.
    1370           0 :     nsAutoJSString label;
    1371           0 :     if (!label.init(cx, args[0])) {
    1372           0 :       LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Invalid string parameter"));
    1373           0 :       return true;
    1374             :     }
    1375             : 
    1376             :     // Get label id value.
    1377           0 :     nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
    1378           0 :     if (NS_FAILED(rv)) {
    1379           0 :       LogToBrowserConsole(nsIScriptError::errorFlag,
    1380           0 :                           NS_LITERAL_STRING("Unknown label for categorical histogram"));
    1381           0 :       return true;
    1382             :     }
    1383             :   } else {
    1384             :     // All other accumulations expect one numerical argument.
    1385           9 :     if (!args.length()) {
    1386           0 :       LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Expected one argument"));
    1387           0 :       return true;
    1388             :     }
    1389             : 
    1390           9 :     if (!(args[0].isNumber() || args[0].isBoolean())) {
    1391           0 :       LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a number"));
    1392           0 :       return true;
    1393             :     }
    1394             : 
    1395           9 :     if (!JS::ToUint32(cx, args[0], &value)) {
    1396           0 :       LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to convert argument"));
    1397           0 :       return true;
    1398             :     }
    1399             :   }
    1400             : 
    1401             :   {
    1402          18 :     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1403           9 :     internal_Accumulate(*h, value);
    1404             :   }
    1405           9 :   return true;
    1406             : }
    1407             : 
    1408             : bool
    1409           0 : internal_JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
    1410             : {
    1411           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1412           0 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
    1413           0 :   if (!obj ||
    1414           0 :       JS_GetClass(obj) != &sJSHistogramClass) {
    1415           0 :     return false;
    1416             :   }
    1417             : 
    1418           0 :   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
    1419           0 :   JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx));
    1420           0 :   if (!snapshot)
    1421           0 :     return false;
    1422             : 
    1423           0 :   switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
    1424             :   case REFLECT_FAILURE:
    1425           0 :     return false;
    1426             :   case REFLECT_CORRUPT:
    1427           0 :     JS_ReportErrorASCII(cx, "Histogram is corrupt");
    1428           0 :     return false;
    1429             :   case REFLECT_OK:
    1430           0 :     args.rval().setObject(*snapshot);
    1431           0 :     return true;
    1432             :   default:
    1433           0 :     MOZ_CRASH("unhandled reflection status");
    1434             :   }
    1435             : }
    1436             : 
    1437             : bool
    1438           0 : internal_JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
    1439             : {
    1440           0 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
    1441           0 :   if (!obj ||
    1442           0 :       JS_GetClass(obj) != &sJSHistogramClass) {
    1443           0 :     return false;
    1444             :   }
    1445             : 
    1446           0 :   bool onlySubsession = false;
    1447           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1448             :   // This function should always return |undefined| and never fail but
    1449             :   // rather report failures using the console.
    1450           0 :   args.rval().setUndefined();
    1451             : 
    1452             : 
    1453             : #if !defined(MOZ_WIDGET_ANDROID)
    1454           0 :   if (args.length() >= 1) {
    1455           0 :     if (!args[0].isBoolean()) {
    1456           0 :       JS_ReportErrorASCII(cx, "Not a boolean");
    1457           0 :       return false;
    1458             :     }
    1459             : 
    1460           0 :     onlySubsession = JS::ToBoolean(args[0]);
    1461             :   }
    1462             : #endif
    1463             : 
    1464           0 :   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
    1465           0 :   MOZ_ASSERT(h);
    1466           0 :   if (h) {
    1467           0 :     internal_HistogramClear(*h, onlySubsession);
    1468             :   }
    1469             : 
    1470           0 :   return true;
    1471             : }
    1472             : 
    1473             : // NOTE: Runs without protection from |gTelemetryHistogramMutex|.
    1474             : // See comment at the top of this section.
    1475             : nsresult
    1476           9 : internal_WrapAndReturnHistogram(Histogram *h, JSContext *cx,
    1477             :                                 JS::MutableHandle<JS::Value> ret)
    1478             : {
    1479          18 :   JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &sJSHistogramClass));
    1480           9 :   if (!obj)
    1481           0 :     return NS_ERROR_FAILURE;
    1482             :   // The 3 functions that are wrapped up here are eventually called
    1483             :   // by the same thread that runs this function.
    1484          36 :   if (!(JS_DefineFunction(cx, obj, "add", internal_JSHistogram_Add, 1, 0)
    1485          18 :         && JS_DefineFunction(cx, obj, "snapshot",
    1486             :                              internal_JSHistogram_Snapshot, 0, 0)
    1487          27 :         && JS_DefineFunction(cx, obj, "clear", internal_JSHistogram_Clear, 0, 0))) {
    1488           0 :     return NS_ERROR_FAILURE;
    1489             :   }
    1490           9 :   JS_SetPrivate(obj, h);
    1491           9 :   ret.setObject(*obj);
    1492           9 :   return NS_OK;
    1493             : }
    1494             : 
    1495             : } // namespace
    1496             : 
    1497             : 
    1498             : ////////////////////////////////////////////////////////////////////////
    1499             : ////////////////////////////////////////////////////////////////////////
    1500             : //
    1501             : // PRIVATE: JSKeyedHistogram_* functions
    1502             : 
    1503             : // NOTE: the functions in this section:
    1504             : //
    1505             : //   internal_KeyedHistogram_SnapshotImpl
    1506             : //   internal_JSKeyedHistogram_Add
    1507             : //   internal_JSKeyedHistogram_Keys
    1508             : //   internal_JSKeyedHistogram_Snapshot
    1509             : //   internal_JSKeyedHistogram_SubsessionSnapshot
    1510             : //   internal_JSKeyedHistogram_SnapshotSubsessionAndClear
    1511             : //   internal_JSKeyedHistogram_Clear
    1512             : //   internal_WrapAndReturnKeyedHistogram
    1513             : //
    1514             : // Same comments as above, at the JSHistogram_* section, regarding
    1515             : // deadlock avoidance, apply.
    1516             : 
    1517             : namespace {
    1518             : 
    1519             : static const JSClass sJSKeyedHistogramClass = {
    1520             :   "JSKeyedHistogram",  /* name */
    1521             :   JSCLASS_HAS_PRIVATE  /* flags */
    1522             : };
    1523             : 
    1524             : bool
    1525           0 : internal_KeyedHistogram_SnapshotImpl(JSContext *cx, unsigned argc,
    1526             :                                      JS::Value *vp,
    1527             :                                      bool subsession, bool clearSubsession)
    1528             : {
    1529           0 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
    1530           0 :   if (!obj ||
    1531           0 :       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
    1532           0 :     return false;
    1533             :   }
    1534             : 
    1535           0 :   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
    1536           0 :   if (!keyed) {
    1537           0 :     return false;
    1538             :   }
    1539             : 
    1540           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1541             : 
    1542           0 :   if (args.length() == 0) {
    1543           0 :     JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
    1544           0 :     if (!snapshot) {
    1545           0 :       JS_ReportErrorASCII(cx, "Failed to create object");
    1546           0 :       return false;
    1547             :     }
    1548             : 
    1549           0 :     if (!NS_SUCCEEDED(keyed->GetJSSnapshot(cx, snapshot, subsession, clearSubsession))) {
    1550           0 :       JS_ReportErrorASCII(cx, "Failed to reflect keyed histograms");
    1551           0 :       return false;
    1552             :     }
    1553             : 
    1554           0 :     args.rval().setObject(*snapshot);
    1555           0 :     return true;
    1556             :   }
    1557             : 
    1558           0 :   nsAutoJSString key;
    1559           0 :   if (!args[0].isString() || !key.init(cx, args[0])) {
    1560           0 :     JS_ReportErrorASCII(cx, "Not a string");
    1561           0 :     return false;
    1562             :   }
    1563             : 
    1564           0 :   Histogram* h = nullptr;
    1565           0 :   nsresult rv = keyed->GetHistogram(NS_ConvertUTF16toUTF8(key), &h, subsession);
    1566           0 :   if (NS_FAILED(rv)) {
    1567           0 :     JS_ReportErrorASCII(cx, "Failed to get histogram");
    1568           0 :     return false;
    1569             :   }
    1570             : 
    1571           0 :   JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
    1572           0 :   if (!snapshot) {
    1573           0 :     return false;
    1574             :   }
    1575             : 
    1576           0 :   switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
    1577             :   case REFLECT_FAILURE:
    1578           0 :     return false;
    1579             :   case REFLECT_CORRUPT:
    1580           0 :     JS_ReportErrorASCII(cx, "Histogram is corrupt");
    1581           0 :     return false;
    1582             :   case REFLECT_OK:
    1583           0 :     args.rval().setObject(*snapshot);
    1584           0 :     return true;
    1585             :   default:
    1586           0 :     MOZ_CRASH("unhandled reflection status");
    1587             :   }
    1588             : }
    1589             : 
    1590             : bool
    1591           6 : internal_JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
    1592             : {
    1593           6 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
    1594          12 :   if (!obj ||
    1595           6 :       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
    1596           0 :     return false;
    1597             :   }
    1598             : 
    1599           6 :   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
    1600           6 :   if (!keyed) {
    1601           0 :     return false;
    1602             :   }
    1603             : 
    1604           6 :   JS::CallArgs args = CallArgsFromVp(argc, vp);
    1605             :   // This function should always return |undefined| and never fail but
    1606             :   // rather report failures using the console.
    1607           6 :   args.rval().setUndefined();
    1608           6 :   if (args.length() < 1) {
    1609           0 :     LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Expected one argument"));
    1610           0 :     return true;
    1611             :   }
    1612             : 
    1613          12 :   nsAutoJSString key;
    1614           6 :   if (!args[0].isString() || !key.init(cx, args[0])) {
    1615           0 :     LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a string"));
    1616           0 :     return true;
    1617             :   }
    1618             : 
    1619           6 :   const uint32_t type = keyed->GetHistogramType();
    1620             : 
    1621             :   // If we don't have an argument for the count histogram, assume an increment of 1.
    1622             :   // Otherwise, make sure to run some sanity checks on the argument.
    1623           6 :   uint32_t value = 1;
    1624           6 :   if ((type != nsITelemetry::HISTOGRAM_COUNT) || (args.length() == 2)) {
    1625           6 :     if (args.length() < 2) {
    1626           0 :       LogToBrowserConsole(nsIScriptError::errorFlag,
    1627           0 :                           NS_LITERAL_STRING("Expected two arguments for this histogram type"));
    1628           0 :       return true;
    1629             :     }
    1630             : 
    1631           6 :     if (type == nsITelemetry::HISTOGRAM_CATEGORICAL && args[1].isString()) {
    1632             :       // For categorical histograms we allow passing a string argument that specifies the label.
    1633             :       mozilla::Telemetry::HistogramID id;
    1634           0 :       if (NS_FAILED(keyed->GetEnumId(id))) {
    1635           0 :         LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to get histogram id."));
    1636           0 :         return true;
    1637             :       }
    1638             : 
    1639             :       // Get label string.
    1640           0 :       nsAutoJSString label;
    1641           0 :       if (!label.init(cx, args[1])) {
    1642           0 :         LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Invalid string parameter"));
    1643           0 :         return true;
    1644             :       }
    1645             : 
    1646             :       // Get label id value.
    1647           0 :       nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
    1648           0 :       if (NS_FAILED(rv)) {
    1649           0 :         LogToBrowserConsole(nsIScriptError::errorFlag,
    1650           0 :                             NS_LITERAL_STRING("Unknown label for categorical histogram"));
    1651           0 :         return true;
    1652             :       }
    1653             :     } else {
    1654             :       // All other accumulations expect one numerical argument.
    1655           6 :       if (!(args[1].isNumber() || args[1].isBoolean())) {
    1656           0 :         LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a number"));
    1657           0 :         return true;
    1658             :       }
    1659             : 
    1660           6 :       if (!JS::ToUint32(cx, args[1], &value)) {
    1661           0 :         LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to convert argument"));
    1662           0 :         return true;
    1663             :       }
    1664             :     }
    1665             :   }
    1666             : 
    1667             :   {
    1668          12 :     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1669           6 :     internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value);
    1670             :   }
    1671           6 :   return true;
    1672             : }
    1673             : 
    1674             : bool
    1675           0 : internal_JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp)
    1676             : {
    1677           0 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
    1678           0 :   if (!obj ||
    1679           0 :       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
    1680           0 :     return false;
    1681             :   }
    1682             : 
    1683           0 :   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
    1684           0 :   if (!keyed) {
    1685           0 :     return false;
    1686             :   }
    1687             : 
    1688           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1689           0 :   return NS_SUCCEEDED(keyed->GetJSKeys(cx, args));
    1690             : }
    1691             : 
    1692             : bool
    1693           0 : internal_JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
    1694             : {
    1695           0 :   return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, false, false);
    1696             : }
    1697             : 
    1698             : #if !defined(MOZ_WIDGET_ANDROID)
    1699             : bool
    1700           0 : internal_JSKeyedHistogram_SubsessionSnapshot(JSContext *cx,
    1701             :                                              unsigned argc, JS::Value *vp)
    1702             : {
    1703           0 :   return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, true, false);
    1704             : }
    1705             : #endif
    1706             : 
    1707             : #if !defined(MOZ_WIDGET_ANDROID)
    1708             : bool
    1709           0 : internal_JSKeyedHistogram_SnapshotSubsessionAndClear(JSContext *cx,
    1710             :                                                      unsigned argc,
    1711             :                                                      JS::Value *vp)
    1712             : {
    1713           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1714           0 :   if (args.length() != 0) {
    1715           0 :     JS_ReportErrorASCII(cx, "No key arguments supported for snapshotSubsessionAndClear");
    1716             :   }
    1717             : 
    1718           0 :   return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, true, true);
    1719             : }
    1720             : #endif
    1721             : 
    1722             : bool
    1723           0 : internal_JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
    1724             : {
    1725           0 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
    1726           0 :   if (!obj ||
    1727           0 :       JS_GetClass(obj) != &sJSKeyedHistogramClass) {
    1728           0 :     return false;
    1729             :   }
    1730             : 
    1731           0 :   KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
    1732           0 :   if (!keyed) {
    1733           0 :     return false;
    1734             :   }
    1735             : 
    1736           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1737             :   // This function should always return |undefined| and never fail but
    1738             :   // rather report failures using the console.
    1739           0 :   args.rval().setUndefined();
    1740             : 
    1741             : #if !defined(MOZ_WIDGET_ANDROID)
    1742           0 :   bool onlySubsession = false;
    1743             : 
    1744           0 :   if (args.length() >= 1) {
    1745           0 :     if (!(args[0].isNumber() || args[0].isBoolean())) {
    1746           0 :       JS_ReportErrorASCII(cx, "Not a boolean");
    1747           0 :       return false;
    1748             :     }
    1749             : 
    1750           0 :     onlySubsession = JS::ToBoolean(args[0]);
    1751             :   }
    1752             : 
    1753           0 :   keyed->Clear(onlySubsession);
    1754             : #else
    1755             :   keyed->Clear(false);
    1756             : #endif
    1757           0 :   return true;
    1758             : }
    1759             : 
    1760             : // NOTE: Runs without protection from |gTelemetryHistogramMutex|.
    1761             : // See comment at the top of this section.
    1762             : nsresult
    1763           6 : internal_WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx,
    1764             :                                      JS::MutableHandle<JS::Value> ret)
    1765             : {
    1766          12 :   JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &sJSKeyedHistogramClass));
    1767           6 :   if (!obj)
    1768           0 :     return NS_ERROR_FAILURE;
    1769             :   // The 6 functions that are wrapped up here are eventually called
    1770             :   // by the same thread that runs this function.
    1771          24 :   if (!(JS_DefineFunction(cx, obj, "add", internal_JSKeyedHistogram_Add, 2, 0)
    1772          12 :         && JS_DefineFunction(cx, obj, "snapshot",
    1773             :                              internal_JSKeyedHistogram_Snapshot, 1, 0)
    1774             : #if !defined(MOZ_WIDGET_ANDROID)
    1775          12 :         && JS_DefineFunction(cx, obj, "subsessionSnapshot",
    1776             :                              internal_JSKeyedHistogram_SubsessionSnapshot, 1, 0)
    1777          12 :         && JS_DefineFunction(cx, obj, "snapshotSubsessionAndClear",
    1778             :                              internal_JSKeyedHistogram_SnapshotSubsessionAndClear, 0, 0)
    1779             : #endif
    1780          12 :         && JS_DefineFunction(cx, obj, "keys",
    1781             :                              internal_JSKeyedHistogram_Keys, 0, 0)
    1782          18 :         && JS_DefineFunction(cx, obj, "clear",
    1783             :                              internal_JSKeyedHistogram_Clear, 0, 0))) {
    1784           0 :     return NS_ERROR_FAILURE;
    1785             :   }
    1786             : 
    1787           6 :   JS_SetPrivate(obj, h);
    1788           6 :   ret.setObject(*obj);
    1789           6 :   return NS_OK;
    1790             : }
    1791             : 
    1792             : } // namespace
    1793             : 
    1794             : 
    1795             : ////////////////////////////////////////////////////////////////////////
    1796             : ////////////////////////////////////////////////////////////////////////
    1797             : //
    1798             : // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryHistogram::
    1799             : 
    1800             : // All of these functions are actually in namespace TelemetryHistogram::,
    1801             : // but the ::TelemetryHistogram prefix is given explicitly.  This is
    1802             : // because it is critical to see which calls from these functions are
    1803             : // to another function in this interface.  Mis-identifying "inwards
    1804             : // calls" from "calls to another function in this interface" will lead
    1805             : // to deadlocking and/or races.  See comments at the top of the file
    1806             : // for further (important!) details.
    1807             : 
    1808             : // Create and destroy the singleton StatisticsRecorder object.
    1809           3 : void TelemetryHistogram::CreateStatisticsRecorder()
    1810             : {
    1811           6 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1812           3 :   MOZ_ASSERT(!gStatisticsRecorder);
    1813           3 :   gStatisticsRecorder = new base::StatisticsRecorder();
    1814           3 : }
    1815             : 
    1816           0 : void TelemetryHistogram::DestroyStatisticsRecorder()
    1817             : {
    1818           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1819           0 :   MOZ_ASSERT(gStatisticsRecorder);
    1820           0 :   if (gStatisticsRecorder) {
    1821           0 :     delete gStatisticsRecorder;
    1822           0 :     gStatisticsRecorder = nullptr;
    1823             :   }
    1824           0 : }
    1825             : 
    1826           3 : void TelemetryHistogram::InitializeGlobalState(bool canRecordBase,
    1827             :                                                bool canRecordExtended)
    1828             : {
    1829           6 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1830           3 :   MOZ_ASSERT(!gInitDone, "TelemetryHistogram::InitializeGlobalState "
    1831             :              "may only be called once");
    1832             : 
    1833           3 :   gCanRecordBase = canRecordBase;
    1834           3 :   gCanRecordExtended = canRecordExtended;
    1835             : 
    1836             :   // gHistogramMap should have been pre-sized correctly at the
    1837             :   // declaration point further up in this file.
    1838             : 
    1839             :   // Populate the static histogram name->id cache.
    1840             :   // Note that the histogram names are statically allocated.
    1841        5193 :   for (uint32_t i = 0; i < mozilla::Telemetry::HistogramCount; i++) {
    1842        5190 :     CharPtrEntryType *entry = gHistogramMap.PutEntry(gHistograms[i].id());
    1843        5190 :     entry->mData = (mozilla::Telemetry::HistogramID) i;
    1844             :   }
    1845             : 
    1846             : #ifdef DEBUG
    1847           3 :   gHistogramMap.MarkImmutable();
    1848             : #endif
    1849             : 
    1850           3 :   mozilla::PodArrayZero(gCorruptHistograms);
    1851             : 
    1852             :   // Create registered keyed histograms
    1853        5193 :   for (const auto & h : gHistograms) {
    1854        5190 :     if (!h.keyed) {
    1855        4809 :       continue;
    1856             :     }
    1857             : 
    1858         762 :     const nsDependentCString id(h.id());
    1859         762 :     const nsDependentCString expiration(h.expiration());
    1860        1143 :     gKeyedHistograms.Put(id, new KeyedHistogram(id, expiration, h.histogramType,
    1861        1143 :                                                 h.min, h.max, h.bucketCount, h.dataset));
    1862         381 :     if (XRE_IsParentProcess()) {
    1863             :       // We must create registered child keyed histograms as well or else the
    1864             :       // same code in TelemetrySession.jsm that fails without parent keyed
    1865             :       // histograms will fail without child keyed histograms.
    1866         254 :       nsCString contentId(id);
    1867         127 :       contentId.AppendLiteral(CONTENT_HISTOGRAM_SUFFIX);
    1868             :       gKeyedHistograms.Put(contentId,
    1869         381 :                            new KeyedHistogram(id, expiration, h.histogramType,
    1870         381 :                                               h.min, h.max, h.bucketCount, h.dataset));
    1871             : 
    1872         254 :       nsCString gpuId(id);
    1873         127 :       gpuId.AppendLiteral(GPU_HISTOGRAM_SUFFIX);
    1874             :       gKeyedHistograms.Put(gpuId,
    1875         381 :                            new KeyedHistogram(id, expiration, h.histogramType,
    1876         381 :                                               h.min, h.max, h.bucketCount, h.dataset));
    1877             : 
    1878         254 :       nsCString extensionId(id);
    1879         127 :       extensionId.AppendLiteral(EXTENSION_HISTOGRAM_SUFFIX);
    1880             :       gKeyedHistograms.Put(extensionId,
    1881         381 :                            new KeyedHistogram(id, expiration, h.histogramType,
    1882         381 :                                               h.min, h.max, h.bucketCount, h.dataset));
    1883             :     }
    1884             :   }
    1885             : 
    1886             :     // Some Telemetry histograms depend on the value of C++ constants and hardcode
    1887             :     // their values in Histograms.json.
    1888             :     // We add static asserts here for those values to match so that future changes
    1889             :     // don't go unnoticed.
    1890             :     static_assert((JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
    1891             :                         gHistograms[mozilla::Telemetry::GC_MINOR_REASON].bucketCount &&
    1892             :                   (JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
    1893             :                         gHistograms[mozilla::Telemetry::GC_MINOR_REASON_LONG].bucketCount &&
    1894             :                   (JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
    1895             :                         gHistograms[mozilla::Telemetry::GC_REASON_2].bucketCount,
    1896             :                   "NUM_TELEMETRY_REASONS is assumed to be a fixed value in Histograms.json."
    1897             :                   " If this was an intentional change, update the n_values for the "
    1898             :                   "following in Histograms.json: GC_MINOR_REASON, GC_MINOR_REASON_LONG, "
    1899             :                   "GC_REASON_2");
    1900             : 
    1901             :     static_assert((mozilla::StartupTimeline::MAX_EVENT_ID + 1) ==
    1902             :                         gHistograms[mozilla::Telemetry::STARTUP_MEASUREMENT_ERRORS].bucketCount,
    1903             :                   "MAX_EVENT_ID is assumed to be a fixed value in Histograms.json.  If this"
    1904             :                   " was an intentional change, update the n_values for the following in "
    1905             :                   "Histograms.json: STARTUP_MEASUREMENT_ERRORS");
    1906             : 
    1907             : 
    1908           3 :   gInitDone = true;
    1909           3 : }
    1910             : 
    1911           0 : void TelemetryHistogram::DeInitializeGlobalState()
    1912             : {
    1913           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1914           0 :   gCanRecordBase = false;
    1915           0 :   gCanRecordExtended = false;
    1916           0 :   gHistogramMap.Clear();
    1917           0 :   gKeyedHistograms.Clear();
    1918           0 :   gInitDone = false;
    1919           0 : }
    1920             : 
    1921             : #ifdef DEBUG
    1922           3 : bool TelemetryHistogram::GlobalStateHasBeenInitialized() {
    1923           6 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1924           6 :   return gInitDone;
    1925             : }
    1926             : #endif
    1927             : 
    1928             : bool
    1929           0 : TelemetryHistogram::CanRecordBase() {
    1930           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1931           0 :   return internal_CanRecordBase();
    1932             : }
    1933             : 
    1934             : void
    1935           0 : TelemetryHistogram::SetCanRecordBase(bool b) {
    1936           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1937           0 :   gCanRecordBase = b;
    1938           0 : }
    1939             : 
    1940             : bool
    1941           0 : TelemetryHistogram::CanRecordExtended() {
    1942           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1943           0 :   return internal_CanRecordExtended();
    1944             : }
    1945             : 
    1946             : void
    1947           3 : TelemetryHistogram::SetCanRecordExtended(bool b) {
    1948           6 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1949           3 :   gCanRecordExtended = b;
    1950           3 : }
    1951             : 
    1952             : 
    1953             : void
    1954           3 : TelemetryHistogram::InitHistogramRecordingEnabled()
    1955             : {
    1956           6 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1957           3 :   auto processType = XRE_GetProcessType();
    1958        5193 :   for (size_t i = 0; i < mozilla::ArrayLength(gHistograms); ++i) {
    1959        5190 :     const HistogramInfo& h = gHistograms[i];
    1960        5190 :     mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
    1961        5190 :     internal_SetHistogramRecordingEnabled(id,
    1962        5190 :                                           CanRecordInProcess(h.record_in_processes,
    1963        5190 :                                                              processType));
    1964             :   }
    1965             : 
    1966          12 :   for (auto recordingInitiallyDisabledID : kRecordingInitiallyDisabledIDs) {
    1967             :     internal_SetHistogramRecordingEnabled(recordingInitiallyDisabledID,
    1968           9 :                                           false);
    1969             :   }
    1970           3 : }
    1971             : 
    1972             : void
    1973           0 : TelemetryHistogram::SetHistogramRecordingEnabled(mozilla::Telemetry::HistogramID aID,
    1974             :                                                  bool aEnabled)
    1975             : {
    1976           0 :   if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
    1977           0 :     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
    1978             :     return;
    1979             :   }
    1980             : 
    1981           0 :   const HistogramInfo& h = gHistograms[aID];
    1982           0 :   if (!CanRecordInProcess(h.record_in_processes, XRE_GetProcessType())) {
    1983             :     // Don't permit record_in_process-disabled recording to be re-enabled.
    1984           0 :     return;
    1985             :   }
    1986             : 
    1987           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1988           0 :   internal_SetHistogramRecordingEnabled(aID, aEnabled);
    1989             : }
    1990             : 
    1991             : 
    1992             : nsresult
    1993           0 : TelemetryHistogram::SetHistogramRecordingEnabled(const nsACString &id,
    1994             :                                                  bool aEnabled)
    1995             : {
    1996           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    1997             : 
    1998             :   mozilla::Telemetry::HistogramID hId;
    1999           0 :   nsresult rv = internal_GetHistogramEnumId(PromiseFlatCString(id).get(), &hId);
    2000           0 :   if (NS_FAILED(rv)) {
    2001           0 :     return rv;
    2002             :   }
    2003           0 :   const HistogramInfo& hi = gHistograms[hId];
    2004           0 :   if (!CanRecordInProcess(hi.record_in_processes, XRE_GetProcessType())) {
    2005           0 :     return NS_OK;
    2006             :   }
    2007             : 
    2008             :   Histogram *h;
    2009           0 :   rv = internal_GetHistogramByName(id, &h);
    2010           0 :   if (NS_SUCCEEDED(rv)) {
    2011           0 :     h->SetRecordingEnabled(aEnabled);
    2012           0 :     return NS_OK;
    2013             :   }
    2014             : 
    2015           0 :   KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
    2016           0 :   if (keyed) {
    2017           0 :     keyed->SetRecordingEnabled(aEnabled);
    2018           0 :     return NS_OK;
    2019             :   }
    2020             : 
    2021           0 :   return NS_ERROR_FAILURE;
    2022             : }
    2023             : 
    2024             : 
    2025             : void
    2026        2784 : TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID,
    2027             :                                uint32_t aSample)
    2028             : {
    2029        2784 :   if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
    2030           0 :     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
    2031             :     return;
    2032             :   }
    2033             : 
    2034        5568 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2035        2784 :   internal_Accumulate(aID, aSample);
    2036        2784 : }
    2037             : 
    2038             : void
    2039        1284 : TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID,
    2040             :                                const nsCString& aKey, uint32_t aSample)
    2041             : {
    2042        1284 :   if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
    2043           0 :     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
    2044             :     return;
    2045             :   }
    2046             : 
    2047        2568 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2048        1284 :   internal_Accumulate(aID, aKey, aSample);
    2049        1284 : }
    2050             : 
    2051             : void
    2052           0 : TelemetryHistogram::Accumulate(const char* name, uint32_t sample)
    2053             : {
    2054           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2055           0 :   if (!internal_CanRecordBase()) {
    2056           0 :     return;
    2057             :   }
    2058             :   mozilla::Telemetry::HistogramID id;
    2059           0 :   nsresult rv = internal_GetHistogramEnumId(name, &id);
    2060           0 :   if (NS_FAILED(rv)) {
    2061           0 :     return;
    2062             :   }
    2063           0 :   internal_Accumulate(id, sample);
    2064             : }
    2065             : 
    2066             : void
    2067           0 : TelemetryHistogram::Accumulate(const char* name,
    2068             :                                const nsCString& key, uint32_t sample)
    2069             : {
    2070           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2071           0 :   if (!internal_CanRecordBase()) {
    2072           0 :     return;
    2073             :   }
    2074             :   mozilla::Telemetry::HistogramID id;
    2075           0 :   nsresult rv = internal_GetHistogramEnumId(name, &id);
    2076           0 :   if (NS_SUCCEEDED(rv)) {
    2077           0 :     internal_Accumulate(id, key, sample);
    2078             :   }
    2079             : }
    2080             : 
    2081             : void
    2082           0 : TelemetryHistogram::AccumulateCategorical(mozilla::Telemetry::HistogramID aId,
    2083             :                                           const nsCString& label)
    2084             : {
    2085           0 :   if (NS_WARN_IF(!internal_IsHistogramEnumId(aId))) {
    2086           0 :     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
    2087             :     return;
    2088             :   }
    2089             : 
    2090           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2091           0 :   if (!internal_CanRecordBase()) {
    2092           0 :     return;
    2093             :   }
    2094           0 :   uint32_t labelId = 0;
    2095           0 :   if (NS_FAILED(gHistograms[aId].label_id(label.get(), &labelId))) {
    2096           0 :     return;
    2097             :   }
    2098           0 :   internal_Accumulate(aId, labelId);
    2099             : }
    2100             : 
    2101             : void
    2102           2 : TelemetryHistogram::AccumulateChild(ProcessID aProcessType,
    2103             :                                     const nsTArray<Accumulation>& aAccumulations)
    2104             : {
    2105           2 :   MOZ_ASSERT(XRE_IsParentProcess());
    2106             : 
    2107           4 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2108           2 :   if (!internal_CanRecordBase()) {
    2109           0 :     return;
    2110             :   }
    2111         532 :   for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
    2112         530 :     if (NS_WARN_IF(!internal_IsHistogramEnumId(aAccumulations[i].mId))) {
    2113           0 :       MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
    2114             :       continue;
    2115             :     }
    2116         530 :     internal_AccumulateChild(aProcessType, aAccumulations[i].mId, aAccumulations[i].mSample);
    2117             :   }
    2118             : }
    2119             : 
    2120             : void
    2121           2 : TelemetryHistogram::AccumulateChildKeyed(ProcessID aProcessType,
    2122             :                                          const nsTArray<KeyedAccumulation>& aAccumulations)
    2123             : {
    2124           2 :   MOZ_ASSERT(XRE_IsParentProcess());
    2125           4 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2126           2 :   if (!internal_CanRecordBase()) {
    2127           0 :     return;
    2128             :   }
    2129         179 :   for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
    2130         177 :     if (NS_WARN_IF(!internal_IsHistogramEnumId(aAccumulations[i].mId))) {
    2131           0 :       MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
    2132             :       continue;
    2133             :     }
    2134         177 :     internal_AccumulateChildKeyed(aProcessType,
    2135         177 :                                   aAccumulations[i].mId,
    2136         177 :                                   aAccumulations[i].mKey,
    2137         354 :                                   aAccumulations[i].mSample);
    2138             :   }
    2139             : }
    2140             : 
    2141             : nsresult
    2142           9 : TelemetryHistogram::GetHistogramById(const nsACString &name, JSContext *cx,
    2143             :                                      JS::MutableHandle<JS::Value> ret)
    2144             : {
    2145           9 :   Histogram *h = nullptr;
    2146             :   {
    2147          18 :     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2148           9 :     nsresult rv = internal_GetHistogramByName(name, &h);
    2149           9 :     if (NS_FAILED(rv))
    2150           0 :       return rv;
    2151             :   }
    2152             :   // Runs without protection from |gTelemetryHistogramMutex|
    2153           9 :   return internal_WrapAndReturnHistogram(h, cx, ret);
    2154             : }
    2155             : 
    2156             : nsresult
    2157           6 : TelemetryHistogram::GetKeyedHistogramById(const nsACString &name,
    2158             :                                           JSContext *cx,
    2159             :                                           JS::MutableHandle<JS::Value> ret)
    2160             : {
    2161           6 :   KeyedHistogram* keyed = nullptr;
    2162             :   {
    2163          12 :     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2164           6 :     if (!gKeyedHistograms.Get(name, &keyed)) {
    2165           0 :       return NS_ERROR_FAILURE;
    2166             :     }
    2167             :   }
    2168             :   // Runs without protection from |gTelemetryHistogramMutex|
    2169           6 :   return internal_WrapAndReturnKeyedHistogram(keyed, cx, ret);
    2170             : }
    2171             : 
    2172             : const char*
    2173           0 : TelemetryHistogram::GetHistogramName(mozilla::Telemetry::HistogramID id)
    2174             : {
    2175           0 :   if (NS_WARN_IF(!internal_IsHistogramEnumId(id))) {
    2176           0 :     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
    2177             :     return nullptr;
    2178             :   }
    2179             : 
    2180           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2181           0 :   const HistogramInfo& h = gHistograms[id];
    2182           0 :   return h.id();
    2183             : }
    2184             : 
    2185             : nsresult
    2186           0 : TelemetryHistogram::CreateHistogramSnapshots(JSContext *cx,
    2187             :                                              JS::MutableHandle<JS::Value> ret,
    2188             :                                              bool subsession,
    2189             :                                              bool clearSubsession)
    2190             : {
    2191             :   // Runs without protection from |gTelemetryHistogramMutex|
    2192           0 :   JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
    2193           0 :   if (!root_obj)
    2194           0 :     return NS_ERROR_FAILURE;
    2195           0 :   ret.setObject(*root_obj);
    2196             : 
    2197             :   // Include the GPU process in histogram snapshots only if we actually tried
    2198             :   // to launch a process for it.
    2199           0 :   bool includeGPUProcess = false;
    2200           0 :   if (auto gpm = mozilla::gfx::GPUProcessManager::Get()) {
    2201           0 :     includeGPUProcess = gpm->AttemptedGPUProcess();
    2202             :   }
    2203             : 
    2204             :   // Ensure that all the HISTOGRAM_FLAG & HISTOGRAM_COUNT histograms have
    2205             :   // been created, so that their values are snapshotted.
    2206           0 :   auto processType = XRE_GetProcessType();
    2207           0 :   for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
    2208           0 :     const HistogramInfo& hi = gHistograms[i];
    2209           0 :     if (hi.keyed || !CanRecordInProcess(hi.record_in_processes, processType)) {
    2210           0 :       continue;
    2211             :     }
    2212           0 :     const uint32_t type = hi.histogramType;
    2213           0 :     if (type == nsITelemetry::HISTOGRAM_FLAG ||
    2214             :         type == nsITelemetry::HISTOGRAM_COUNT) {
    2215             :       Histogram *h;
    2216           0 :       mozilla::DebugOnly<nsresult> rv;
    2217           0 :       mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
    2218             : 
    2219           0 :       for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
    2220           0 :         if ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess) {
    2221           0 :           continue;
    2222             :         }
    2223           0 :         rv = internal_GetHistogramByEnumId(id, &h, ProcessID(process));
    2224           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    2225             :       }
    2226             :     }
    2227             :   }
    2228             : 
    2229           0 :   StatisticsRecorder::Histograms hs;
    2230           0 :   StatisticsRecorder::GetHistograms(&hs);
    2231             : 
    2232             :   // We identify corrupt histograms first, rather than interspersing it
    2233             :   // in the loop below, to ensure that our corruption statistics don't
    2234             :   // depend on histogram enumeration order.
    2235             :   //
    2236             :   // Of course, we hope that all of these corruption-statistics
    2237             :   // histograms are not themselves corrupt...
    2238           0 :   internal_IdentifyCorruptHistograms(hs);
    2239             : 
    2240             :   // OK, now we can actually reflect things.
    2241           0 :   JS::Rooted<JSObject*> hobj(cx);
    2242           0 :   for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
    2243           0 :     const HistogramInfo& hi = gHistograms[i];
    2244           0 :     if (hi.keyed) {
    2245           0 :       continue;
    2246             :     }
    2247             : 
    2248           0 :     Histogram* h = nullptr;
    2249           0 :     mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
    2250             : 
    2251           0 :     for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
    2252           0 :       if (!CanRecordInProcess(hi.record_in_processes, ProcessID(process)) ||
    2253           0 :         ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess)) {
    2254           0 :         continue;
    2255             :       }
    2256           0 :       nsresult rv = internal_GetHistogramByEnumId(id, &h, ProcessID(process));
    2257           0 :       if (NS_WARN_IF(NS_FAILED(rv)) || !internal_ShouldReflectHistogram(h) ||
    2258           0 :         internal_IsEmpty(h) || internal_IsExpired(h)) {
    2259           0 :         continue;
    2260             :       }
    2261             : 
    2262           0 :       Histogram* original = h;
    2263             : #if !defined(MOZ_WIDGET_ANDROID)
    2264           0 :       if (subsession) {
    2265           0 :         h = internal_GetSubsessionHistogram(*h);
    2266           0 :         if (!h) {
    2267           0 :           continue;
    2268             :         }
    2269             :       }
    2270             : #endif
    2271             : 
    2272           0 :       hobj = JS_NewPlainObject(cx);
    2273           0 :       if (!hobj) {
    2274           0 :         return NS_ERROR_FAILURE;
    2275             :       }
    2276           0 :       switch (internal_ReflectHistogramSnapshot(cx, hobj, h)) {
    2277             :       case REFLECT_CORRUPT:
    2278             :         // We can still hit this case even if ShouldReflectHistograms
    2279             :         // returns true.  The histogram lies outside of our control
    2280             :         // somehow; just skip it.
    2281           0 :         continue;
    2282             :       case REFLECT_FAILURE:
    2283           0 :         return NS_ERROR_FAILURE;
    2284             :       case REFLECT_OK:
    2285           0 :         if (!JS_DefineProperty(cx, root_obj, original->histogram_name().c_str(),
    2286             :                                hobj, JSPROP_ENUMERATE)) {
    2287           0 :           return NS_ERROR_FAILURE;
    2288             :         }
    2289             :       }
    2290             : 
    2291             : #if !defined(MOZ_WIDGET_ANDROID)
    2292           0 :       if (subsession && clearSubsession) {
    2293           0 :         h->Clear();
    2294             :       }
    2295             : #endif
    2296             :     }
    2297             :   }
    2298           0 :   return NS_OK;
    2299             : }
    2300             : 
    2301             : nsresult
    2302           0 : TelemetryHistogram::RegisteredHistograms(uint32_t aDataset, uint32_t *aCount,
    2303             :                                          char*** aHistograms)
    2304             : {
    2305           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2306             :   return internal_GetRegisteredHistogramIds(false,
    2307           0 :                                             aDataset, aCount, aHistograms);
    2308             : }
    2309             : 
    2310             : nsresult
    2311           0 : TelemetryHistogram::RegisteredKeyedHistograms(uint32_t aDataset,
    2312             :                                               uint32_t *aCount,
    2313             :                                               char*** aHistograms)
    2314             : {
    2315           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2316             :   return internal_GetRegisteredHistogramIds(true,
    2317           0 :                                             aDataset, aCount, aHistograms);
    2318             : }
    2319             : 
    2320             : nsresult
    2321           0 : TelemetryHistogram::GetKeyedHistogramSnapshots(JSContext *cx,
    2322             :                                                JS::MutableHandle<JS::Value> ret)
    2323             : {
    2324             :   // Runs without protection from |gTelemetryHistogramMutex|
    2325           0 :   JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
    2326           0 :   if (!obj) {
    2327           0 :     return NS_ERROR_FAILURE;
    2328             :   }
    2329             : 
    2330           0 :   for (auto iter = gKeyedHistograms.Iter(); !iter.Done(); iter.Next()) {
    2331           0 :     JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
    2332           0 :     if (!snapshot) {
    2333           0 :       return NS_ERROR_FAILURE;
    2334             :     }
    2335             : 
    2336           0 :     if (!NS_SUCCEEDED(iter.Data()->GetJSSnapshot(cx, snapshot, false, false))) {
    2337           0 :       return NS_ERROR_FAILURE;
    2338             :     }
    2339             : 
    2340           0 :     if (!JS_DefineProperty(cx, obj, PromiseFlatCString(iter.Key()).get(),
    2341             :                            snapshot, JSPROP_ENUMERATE)) {
    2342           0 :       return NS_ERROR_FAILURE;
    2343             :     }
    2344             :   }
    2345             : 
    2346           0 :   ret.setObject(*obj);
    2347           0 :   return NS_OK;
    2348             : }
    2349             : 
    2350             : size_t
    2351           0 : TelemetryHistogram::GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf
    2352             :                                                       aMallocSizeOf)
    2353             : {
    2354           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2355           0 :   return gHistogramMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
    2356             : }
    2357             : 
    2358             : size_t
    2359           0 : TelemetryHistogram::GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf
    2360             :                                                      aMallocSizeOf)
    2361             : {
    2362           0 :   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
    2363           0 :   StatisticsRecorder::Histograms hs;
    2364           0 :   StatisticsRecorder::GetHistograms(&hs);
    2365           0 :   size_t n = 0;
    2366           0 :   for (auto h : hs) {
    2367           0 :     n += h->SizeOfIncludingThis(aMallocSizeOf);
    2368             :   }
    2369           0 :   return n;
    2370           9 : }

Generated by: LCOV version 1.13