1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 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 "Telemetry.h"
8 #include "TelemetryOrigin.h"
9 
10 #include "nsTHashMap.h"
11 #include "nsIObserverService.h"
12 #include "nsPrintfCString.h"
13 #include "nsTArray.h"
14 #include "TelemetryCommon.h"
15 #include "TelemetryOriginEnums.h"
16 
17 #include "js/Array.h"               // JS::NewArrayObject
18 #include "js/PropertyAndElement.h"  // JS_DefineElement, JS_DefineProperty
19 #include "mozilla/Atomics.h"
20 #include "mozilla/Base64.h"
21 #include "mozilla/dom/PrioEncoder.h"
22 #include "mozilla/Preferences.h"
23 #include "mozilla/Services.h"
24 #include "mozilla/StaticMutex.h"
25 #include "mozilla/Tuple.h"
26 #include "mozilla/UniquePtr.h"
27 
28 #include <cmath>
29 #include <type_traits>
30 
31 using mozilla::Get;
32 using mozilla::MakeTuple;
33 using mozilla::MakeUnique;
34 using mozilla::MallocSizeOf;
35 using mozilla::StaticMutex;
36 using mozilla::StaticMutexAutoLock;
37 using mozilla::Tuple;
38 using mozilla::UniquePtr;
39 using mozilla::dom::PrioEncoder;
40 using mozilla::Telemetry::OriginMetricID;
41 using mozilla::Telemetry::Common::ToJSString;
42 
43 /***********************************************************************
44  *
45  * Firefox Origin Telemetry
46  * Docs:
47  * https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/collection/origin.html
48  *
49  * Origin Telemetry stores pairs of information (metric, origin) which boils
50  * down to "$metric happened on $origin".
51  *
52  * Prio can only encode up-to-2046-length bit vectors. The process of
53  * transforming these pairs of information into bit vectors is called "App
54  * Encoding". The bit vectors are then "Prio Encoded" into binary goop. The
55  * binary goop is then "Base64 Encoded" into strings.
56  *
57  */
58 
59 ////////////////////////////////////////////////////////////////////////
60 ////////////////////////////////////////////////////////////////////////
61 //
62 // PRIVATE TYPES
63 
64 namespace {
65 
66 class OriginMetricIDHashKey : public PLDHashEntryHdr {
67  public:
68   typedef const OriginMetricID& KeyType;
69   typedef const OriginMetricID* KeyTypePointer;
70 
OriginMetricIDHashKey(KeyTypePointer aKey)71   explicit OriginMetricIDHashKey(KeyTypePointer aKey) : mValue(*aKey) {}
OriginMetricIDHashKey(OriginMetricIDHashKey && aOther)72   OriginMetricIDHashKey(OriginMetricIDHashKey&& aOther)
73       : PLDHashEntryHdr(std::move(aOther)), mValue(std::move(aOther.mValue)) {}
74   ~OriginMetricIDHashKey() = default;
75 
GetKey() const76   KeyType GetKey() const { return mValue; }
KeyEquals(KeyTypePointer aKey) const77   bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
78 
KeyToPointer(KeyType aKey)79   static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
HashKey(KeyTypePointer aKey)80   static PLDHashNumber HashKey(KeyTypePointer aKey) {
81     return static_cast<std::underlying_type<OriginMetricID>::type>(*aKey);
82   }
83   enum { ALLOW_MEMMOVE = true };
84 
85  private:
86   const OriginMetricID mValue;
87 };
88 
89 }  // anonymous namespace
90 
91 ////////////////////////////////////////////////////////////////////////
92 ////////////////////////////////////////////////////////////////////////
93 //
94 // PRIVATE STATE, SHARED BY ALL THREADS
95 //
96 // Access for all of this state (except gInitDone) must be guarded by
97 // gTelemetryOriginMutex.
98 
99 namespace {
100 
101 // This is a StaticMutex rather than a plain Mutex (1) so that
102 // it gets initialised in a thread-safe manner the first time
103 // it is used, and (2) because it is never de-initialised, and
104 // a normal Mutex would show up as a leak in BloatView.  StaticMutex
105 // also has the "OffTheBooks" property, so it won't show as a leak
106 // in BloatView.
107 // Another reason to use a StaticMutex instead of a plain Mutex is
108 // that, due to the nature of Telemetry, we cannot rely on having a
109 // mutex initialized in InitializeGlobalState. Unfortunately, we
110 // cannot make sure that no other function is called before this point.
111 static StaticMutex gTelemetryOriginMutex;
112 
113 typedef nsTArray<Tuple<const char*, const char*>> OriginHashesList;
114 UniquePtr<OriginHashesList> gOriginHashesList;
115 
116 typedef nsTHashMap<nsCStringHashKey, size_t> OriginToIndexMap;
117 UniquePtr<OriginToIndexMap> gOriginToIndexMap;
118 
119 typedef nsTHashMap<nsCStringHashKey, size_t> HashToIndexMap;
120 UniquePtr<HashToIndexMap> gHashToIndexMap;
121 
122 typedef nsTHashMap<nsCStringHashKey, uint32_t> OriginBag;
123 typedef nsTHashMap<OriginMetricIDHashKey, OriginBag> IdToOriginBag;
124 
125 UniquePtr<IdToOriginBag> gMetricToOriginBag;
126 
127 mozilla::Atomic<bool, mozilla::Relaxed> gInitDone(false);
128 
129 // Useful for app-encoded data
130 typedef nsTArray<std::pair<OriginMetricID, nsTArray<nsTArray<bool>>>>
131     IdBoolsPairArray;
132 
133 // Prio has a maximum supported number of bools it can encode at a time.
134 // This means a single metric may result in several encoded payloads if the
135 // number of origins exceeds the number of bools.
136 // Each encoded payload corresponds to an element in the `prioData` array in the
137 // "prio" ping.
138 // This number is the number of encoded payloads needed per metric, and is
139 // equivalent to "how many bitvectors do we need to encode this whole list of
140 // origins?"
141 static uint32_t gPrioDatasPerMetric;
142 
143 // The number of "meta-origins": in-band metadata about origin telemetry.
144 // Currently 1: the "unknown origin recorded" meta-origin.
145 static uint32_t kNumMetaOrigins = 1;
146 
147 constexpr auto kUnknownOrigin = "__UNKNOWN__"_ns;
148 
149 }  // namespace
150 
151 ////////////////////////////////////////////////////////////////////////
152 ////////////////////////////////////////////////////////////////////////
153 //
154 // PRIVATE: thread-safe helpers
155 
156 namespace {
157 
GetNameForMetricID(OriginMetricID aId)158 const char* GetNameForMetricID(OriginMetricID aId) {
159   MOZ_ASSERT(aId < OriginMetricID::Count);
160   return mozilla::Telemetry::MetricIDToString[static_cast<uint32_t>(aId)];
161 }
162 
163 // Calculates the number of `prioData` elements we'd need if we were asked for
164 // an encoded snapshot right now.
PrioDataCount(const StaticMutexAutoLock & lock)165 uint32_t PrioDataCount(const StaticMutexAutoLock& lock) {
166   uint32_t count = 0;
167   for (const auto& origins : gMetricToOriginBag->Values()) {
168     uint32_t maxOriginCount = 0;
169     for (const auto& data : origins.Values()) {
170       maxOriginCount = std::max(maxOriginCount, data);
171     }
172     count += gPrioDatasPerMetric * maxOriginCount;
173   }
174   return count;
175 }
176 
177 // Takes the storage and turns it into bool arrays for Prio to encode, turning
178 // { metric1: [origin1, origin2, ...], ...}
179 // into
180 // [(metric1, [[shard1], [shard2], ...]), ...]
181 // Note: if an origin is present multiple times for a given metric, we must
182 // generate multiple (id, boolvectors) pairs so that they are all reported.
183 // Meaning
184 // { metric1: [origin1, origin2, origin2] }
185 // must turn into (with a pretend gNumBooleans of 1)
186 // [(metric1, [[1], [1]]), (metric1, [[0], [1]])]
AppEncodeTo(const StaticMutexAutoLock & lock,IdBoolsPairArray & aResult)187 nsresult AppEncodeTo(const StaticMutexAutoLock& lock,
188                      IdBoolsPairArray& aResult) {
189   for (const auto& bagEntry : *gMetricToOriginBag) {
190     OriginMetricID id = bagEntry.GetKey();
191     const OriginBag& bag = bagEntry.GetData();
192 
193     uint32_t generation = 1;
194     uint32_t maxGeneration = 1;
195     do {
196       // Fill in the result bool vectors with `false`s.
197       nsTArray<nsTArray<bool>> metricData(gPrioDatasPerMetric);
198       metricData.SetLength(gPrioDatasPerMetric);
199       for (size_t i = 0; i < metricData.Length() - 1; ++i) {
200         metricData[i].SetLength(PrioEncoder::gNumBooleans);
201         for (auto& metricDatum : metricData[i]) {
202           metricDatum = false;
203         }
204       }
205       auto& lastArray = metricData[metricData.Length() - 1];
206       lastArray.SetLength((gOriginHashesList->Length() + kNumMetaOrigins) %
207                           PrioEncoder::gNumBooleans);
208       for (auto& metricDatum : lastArray) {
209         metricDatum = false;
210       }
211 
212       for (const auto& originEntry : bag) {
213         uint32_t originCount = originEntry.GetData();
214         if (originCount >= generation) {
215           maxGeneration = std::max(maxGeneration, originCount);
216 
217           const nsACString& origin = originEntry.GetKey();
218           size_t index;
219           if (!gOriginToIndexMap->Get(origin, &index)) {
220             return NS_ERROR_FAILURE;
221           }
222           MOZ_ASSERT(index < (gOriginHashesList->Length() + kNumMetaOrigins));
223           size_t shardIndex = index / PrioEncoder::gNumBooleans;
224           MOZ_ASSERT(shardIndex < metricData.Length());
225           MOZ_ASSERT(index % PrioEncoder::gNumBooleans <
226                      metricData[shardIndex].Length());
227           metricData[shardIndex][index % PrioEncoder::gNumBooleans] = true;
228         }
229       }
230       aResult.EmplaceBack(id, std::move(metricData));
231     } while (generation++ < maxGeneration);
232   }
233   return NS_OK;
234 }
235 
236 }  // anonymous namespace
237 
238 ////////////////////////////////////////////////////////////////////////
239 ////////////////////////////////////////////////////////////////////////
240 //
241 // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryOrigin::
242 
InitializeGlobalState()243 void TelemetryOrigin::InitializeGlobalState() {
244   if (!XRE_IsParentProcess()) {
245     return;
246   }
247 
248   StaticMutexAutoLock locker(gTelemetryOriginMutex);
249 
250   MOZ_ASSERT(!gInitDone,
251              "TelemetryOrigin::InitializeGlobalState "
252              "may only be called once");
253 
254   // The contents and order of the arrays that follow matter.
255   // Both ensure a consistent app-encoding.
256   static const char sOriginStrings[] = {
257 #define ORIGIN(origin, hash) origin "\0"
258 #include "TelemetryOriginData.inc"
259 #undef ORIGIN
260   };
261   static const char sHashStrings[] = {
262 #define ORIGIN(origin, hash) hash "\0"
263 #include "TelemetryOriginData.inc"
264 #undef ORIGIN
265   };
266 
267   struct OriginHashLengths {
268     uint8_t originLength;
269     uint8_t hashLength;
270   };
271   static const OriginHashLengths sOriginHashLengths[] = {
272 #define ORIGIN(origin, hash) {sizeof(origin), sizeof(hash)},
273 #include "TelemetryOriginData.inc"
274 #undef ORIGIN
275   };
276 
277   static const size_t kNumOrigins = MOZ_ARRAY_LENGTH(sOriginHashLengths);
278 
279   gOriginHashesList = MakeUnique<OriginHashesList>(kNumOrigins);
280 
281   gPrioDatasPerMetric =
282       ceil(static_cast<double>(kNumOrigins + kNumMetaOrigins) /
283            PrioEncoder::gNumBooleans);
284 
285   gOriginToIndexMap =
286       MakeUnique<OriginToIndexMap>(kNumOrigins + kNumMetaOrigins);
287   gHashToIndexMap = MakeUnique<HashToIndexMap>(kNumOrigins);
288   size_t originOffset = 0;
289   size_t hashOffset = 0;
290   for (size_t i = 0; i < kNumOrigins; ++i) {
291     const char* origin = &sOriginStrings[originOffset];
292     const char* hash = &sHashStrings[hashOffset];
293     MOZ_ASSERT(!kUnknownOrigin.Equals(origin),
294                "Unknown origin literal is reserved in Origin Telemetry");
295 
296     gOriginHashesList->AppendElement(MakeTuple(origin, hash));
297 
298     const size_t originLength = sOriginHashLengths[i].originLength;
299     const size_t hashLength = sOriginHashLengths[i].hashLength;
300 
301     originOffset += originLength;
302     hashOffset += hashLength;
303 
304     // -1 to leave off the null terminators.
305     gOriginToIndexMap->InsertOrUpdate(
306         nsDependentCString(origin, originLength - 1), i);
307     gHashToIndexMap->InsertOrUpdate(nsDependentCString(hash, hashLength - 1),
308                                     i);
309   }
310 
311   // Add the meta-origin for tracking recordings to untracked origins.
312   gOriginToIndexMap->InsertOrUpdate(kUnknownOrigin,
313                                     gOriginHashesList->Length());
314 
315   gMetricToOriginBag = MakeUnique<IdToOriginBag>();
316 
317   // This map shouldn't change at runtime, so make debug builds complain
318   // if it tries.
319   gOriginToIndexMap->MarkImmutable();
320   gHashToIndexMap->MarkImmutable();
321 
322   gInitDone = true;
323 }
324 
DeInitializeGlobalState()325 void TelemetryOrigin::DeInitializeGlobalState() {
326   if (!XRE_IsParentProcess()) {
327     return;
328   }
329 
330   StaticMutexAutoLock locker(gTelemetryOriginMutex);
331   MOZ_ASSERT(gInitDone);
332 
333   if (!gInitDone) {
334     return;
335   }
336 
337   gOriginHashesList = nullptr;
338 
339   gOriginToIndexMap = nullptr;
340 
341   gHashToIndexMap = nullptr;
342 
343   gMetricToOriginBag = nullptr;
344 
345   gInitDone = false;
346 }
347 
RecordOrigin(OriginMetricID aId,const nsACString & aOrigin)348 nsresult TelemetryOrigin::RecordOrigin(OriginMetricID aId,
349                                        const nsACString& aOrigin) {
350   if (!XRE_IsParentProcess()) {
351     return NS_ERROR_FAILURE;
352   }
353 
354   uint32_t prioDataCount;
355   {
356     StaticMutexAutoLock locker(gTelemetryOriginMutex);
357 
358     // Common Telemetry error-handling practices for recording functions:
359     // only illegal calls return errors whereas merely incorrect ones are mutely
360     // ignored.
361     if (!gInitDone) {
362       return NS_OK;
363     }
364 
365     size_t index;
366     nsCString origin(aOrigin);
367     if (gHashToIndexMap->Get(aOrigin, &index)) {
368       MOZ_ASSERT(aOrigin.Equals(Get<1>((*gOriginHashesList)[index])));
369       origin = Get<0>((*gOriginHashesList)[index]);
370     }
371 
372     if (!gOriginToIndexMap->Contains(origin)) {
373       // Only record one unknown origin per metric per snapshot.
374       // (otherwise we may get swamped and blow our data budget.)
375       if (gMetricToOriginBag->Contains(aId) &&
376           gMetricToOriginBag->LookupOrInsert(aId).Contains(kUnknownOrigin)) {
377         return NS_OK;
378       }
379       origin = kUnknownOrigin;
380     }
381 
382     auto& originBag = gMetricToOriginBag->LookupOrInsert(aId);
383     originBag.LookupOrInsert(origin)++;
384 
385     prioDataCount = PrioDataCount(locker);
386   }
387 
388   static uint32_t sPrioPingLimit =
389       mozilla::Preferences::GetUint("toolkit.telemetry.prioping.dataLimit", 10);
390   if (prioDataCount >= sPrioPingLimit) {
391     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
392     if (os) {
393       // Ensure we don't notify while holding the lock in case of synchronous
394       // dispatch. May deadlock ourselves if we then trigger a snapshot.
395       os->NotifyObservers(nullptr, "origin-telemetry-storage-limit-reached",
396                           nullptr);
397     }
398   }
399 
400   return NS_OK;
401 }
402 
GetOriginSnapshot(bool aClear,JSContext * aCx,JS::MutableHandleValue aResult)403 nsresult TelemetryOrigin::GetOriginSnapshot(bool aClear, JSContext* aCx,
404                                             JS::MutableHandleValue aResult) {
405   if (NS_WARN_IF(!XRE_IsParentProcess())) {
406     return NS_ERROR_FAILURE;
407   }
408 
409   if (!gInitDone) {
410     return NS_OK;
411   }
412 
413   // Step 1: Grab the lock, copy into stack-local storage, optionally clear.
414   IdToOriginBag copy;
415   {
416     StaticMutexAutoLock locker(gTelemetryOriginMutex);
417 
418     if (aClear) {
419       // I'd really prefer to clear after we're sure the snapshot didn't go
420       // awry, but we can't hold a lock preventing recording while using JS
421       // APIs. And replaying any interleaving recording sounds like too much
422       // squeeze for not enough juice.
423 
424       gMetricToOriginBag->SwapElements(copy);
425     } else {
426       for (const auto& entry : *gMetricToOriginBag) {
427         copy.InsertOrUpdate(entry.GetKey(), entry.GetData().Clone());
428       }
429     }
430   }
431 
432   // Step 2: Without the lock, generate JS datastructure for snapshotting
433   JS::Rooted<JSObject*> rootObj(aCx, JS_NewPlainObject(aCx));
434   if (NS_WARN_IF(!rootObj)) {
435     return NS_ERROR_FAILURE;
436   }
437   aResult.setObject(*rootObj);
438   for (const auto& entry : copy) {
439     JS::RootedObject originsObj(aCx, JS_NewPlainObject(aCx));
440     if (NS_WARN_IF(!originsObj)) {
441       return NS_ERROR_FAILURE;
442     }
443     if (!JS_DefineProperty(aCx, rootObj, GetNameForMetricID(entry.GetKey()),
444                            originsObj, JSPROP_ENUMERATE)) {
445       NS_WARNING("Failed to define property in origin snapshot.");
446       return NS_ERROR_FAILURE;
447     }
448 
449     for (const auto& originEntry : entry.GetData()) {
450       if (!JS_DefineProperty(aCx, originsObj,
451                              nsPromiseFlatCString(originEntry.GetKey()).get(),
452                              originEntry.GetData(), JSPROP_ENUMERATE)) {
453         NS_WARNING("Failed to define origin and count in snapshot.");
454         return NS_ERROR_FAILURE;
455       }
456     }
457   }
458 
459   return NS_OK;
460 }
461 
GetEncodedOriginSnapshot(bool aClear,JSContext * aCx,JS::MutableHandleValue aSnapshot)462 nsresult TelemetryOrigin::GetEncodedOriginSnapshot(
463     bool aClear, JSContext* aCx, JS::MutableHandleValue aSnapshot) {
464   if (!XRE_IsParentProcess()) {
465     return NS_ERROR_FAILURE;
466   }
467 
468   if (!gInitDone) {
469     return NS_OK;
470   }
471 
472   // Step 1: Take the lock and app-encode. Optionally clear.
473   nsresult rv;
474   IdBoolsPairArray appEncodedMetricData;
475   {
476     StaticMutexAutoLock lock(gTelemetryOriginMutex);
477 
478     rv = AppEncodeTo(lock, appEncodedMetricData);
479     if (NS_WARN_IF(NS_FAILED(rv))) {
480       return rv;
481     }
482 
483     if (aClear) {
484       // I'd really prefer to clear after we're sure the snapshot didn't go
485       // awry, but we can't hold a lock preventing recording while using JS
486       // APIs. And replaying any interleaving recording sounds like too much
487       // squeeze for not enough juice.
488 
489       gMetricToOriginBag->Clear();
490     }
491   }
492 
493   // Step 2: Don't need the lock to prio-encode and base64-encode
494   nsTArray<std::pair<nsCString, std::pair<nsCString, nsCString>>> prioData;
495   for (auto& metricData : appEncodedMetricData) {
496     auto& boolVectors = metricData.second;
497     for (uint32_t i = 0; i < boolVectors.Length(); ++i) {
498       // "encoding" is of the form `metricName-X` where X is the shard index.
499       nsCString encodingName =
500           nsPrintfCString("%s-%u", GetNameForMetricID(metricData.first), i);
501       nsCString aResult;
502       nsCString bResult;
503       rv = PrioEncoder::EncodeNative(encodingName, boolVectors[i], aResult,
504                                      bResult);
505       if (NS_WARN_IF(NS_FAILED(rv))) {
506         return rv;
507       }
508       nsCString aBase64;
509       rv = mozilla::Base64Encode(aResult, aBase64);
510       if (NS_WARN_IF(NS_FAILED(rv))) {
511         return rv;
512       }
513       nsCString bBase64;
514       rv = mozilla::Base64Encode(bResult, bBase64);
515       if (NS_WARN_IF(NS_FAILED(rv))) {
516         return rv;
517       }
518 
519       prioData.EmplaceBack(std::move(encodingName),
520                            std::pair(std::move(aBase64), std::move(bBase64)));
521     }
522   }
523 
524   // Step 3: Still don't need the lock to translate to JS
525   // The resulting data structure is:
526   // [{
527   //   encoding: <encoding name>,
528   //   prio: {
529   //     a: <base64 string>,
530   //     b: <base64 string>,
531   //   },
532   // }, ...]
533 
534   JS::RootedObject prioDataArray(aCx,
535                                  JS::NewArrayObject(aCx, prioData.Length()));
536   if (NS_WARN_IF(!prioDataArray)) {
537     return NS_ERROR_FAILURE;
538   }
539   uint32_t i = 0;
540   for (auto& prioDatum : prioData) {
541     JS::RootedObject prioDatumObj(aCx, JS_NewPlainObject(aCx));
542     if (NS_WARN_IF(!prioDatumObj)) {
543       return NS_ERROR_FAILURE;
544     }
545     JSString* encoding = ToJSString(aCx, prioDatum.first);
546     JS::RootedString rootedEncoding(aCx, encoding);
547     if (NS_WARN_IF(!JS_DefineProperty(aCx, prioDatumObj, "encoding",
548                                       rootedEncoding, JSPROP_ENUMERATE))) {
549       return NS_ERROR_FAILURE;
550     }
551 
552     JS::RootedObject prioObj(aCx, JS_NewPlainObject(aCx));
553     if (NS_WARN_IF(!prioObj)) {
554       return NS_ERROR_FAILURE;
555     }
556     if (NS_WARN_IF(!JS_DefineProperty(aCx, prioDatumObj, "prio", prioObj,
557                                       JSPROP_ENUMERATE))) {
558       return NS_ERROR_FAILURE;
559     }
560 
561     JS::RootedString aRootStr(aCx, ToJSString(aCx, prioDatum.second.first));
562     if (NS_WARN_IF(!JS_DefineProperty(aCx, prioObj, "a", aRootStr,
563                                       JSPROP_ENUMERATE))) {
564       return NS_ERROR_FAILURE;
565     }
566     JS::RootedString bRootStr(aCx, ToJSString(aCx, prioDatum.second.second));
567     if (NS_WARN_IF(!JS_DefineProperty(aCx, prioObj, "b", bRootStr,
568                                       JSPROP_ENUMERATE))) {
569       return NS_ERROR_FAILURE;
570     }
571 
572     if (NS_WARN_IF(!JS_DefineElement(aCx, prioDataArray, i++, prioDatumObj,
573                                      JSPROP_ENUMERATE))) {
574       return NS_ERROR_FAILURE;
575     }
576   }
577 
578   aSnapshot.setObject(*prioDataArray);
579 
580   return NS_OK;
581 }
582 
583 /**
584  * Resets all the stored events. This is intended to be only used in tests.
585  */
ClearOrigins()586 void TelemetryOrigin::ClearOrigins() {
587   StaticMutexAutoLock lock(gTelemetryOriginMutex);
588 
589   if (!gInitDone) {
590     return;
591   }
592 
593   gMetricToOriginBag->Clear();
594 }
595 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)596 size_t TelemetryOrigin::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
597   StaticMutexAutoLock locker(gTelemetryOriginMutex);
598   size_t n = 0;
599 
600   if (!gInitDone) {
601     return 0;
602   }
603 
604   n += gMetricToOriginBag->ShallowSizeOfIncludingThis(aMallocSizeOf);
605   for (const auto& origins : gMetricToOriginBag->Values()) {
606     // The string hashkey and count should both be contained by the hashtable.
607     n += origins.ShallowSizeOfExcludingThis(aMallocSizeOf);
608   }
609 
610   // The string hashkey and ID should both be contained within the hashtable.
611   n += gOriginToIndexMap->ShallowSizeOfIncludingThis(aMallocSizeOf);
612 
613   return n;
614 }
615 
SizeOfPrioDatasPerMetric()616 size_t TelemetryOrigin::SizeOfPrioDatasPerMetric() {
617   return gPrioDatasPerMetric;
618 }
619