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