Backed out 7 changesets (bug 1942424) for causing frequent crashes. a=backout
[gecko.git] / toolkit / components / telemetry / core / Telemetry.cpp
blob8473b3a191070ac7b9bb05bc991e0cc9247ceca9
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/. */
7 #include "Telemetry.h"
9 #include <algorithm>
10 #include <prio.h>
11 #include <prproces.h>
12 #if defined(XP_UNIX) && !defined(XP_DARWIN)
13 # include <time.h>
14 #else
15 # include <chrono>
16 #endif
17 #include "base/pickle.h"
18 #include "base/process_util.h"
19 #include "ipc/TelemetryIPCAccumulator.h"
20 #include "jsapi.h"
21 #include "jsfriendapi.h"
22 #include "js/Array.h" // JS::NewArrayObject
23 #include "js/GCAPI.h"
24 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineProperty
25 #include "mozilla/dom/ToJSValue.h"
26 #include "mozilla/dom/Promise.h"
27 #include "mozilla/glean/TelemetryMetrics.h"
28 #include "mozilla/Atomics.h"
29 #include "mozilla/Attributes.h"
30 #include "mozilla/BackgroundHangMonitor.h"
31 #ifdef MOZ_BACKGROUNDTASKS
32 # include "mozilla/BackgroundTasks.h"
33 #endif
34 #include "mozilla/Components.h"
35 #include "mozilla/DataMutex.h"
36 #include "mozilla/DebugOnly.h"
37 #include "mozilla/FStream.h"
38 #include "mozilla/IOInterposer.h"
39 #include "mozilla/Likely.h"
40 #include "mozilla/MathAlgorithms.h"
41 #include "mozilla/MemoryReporting.h"
42 #include "mozilla/MemoryTelemetry.h"
43 #include "mozilla/ModuleUtils.h"
44 #include "mozilla/Mutex.h"
45 #include "mozilla/PoisonIOInterposer.h"
46 #include "mozilla/Preferences.h"
47 #include "mozilla/StartupTimeline.h"
48 #include "mozilla/StaticPtr.h"
49 #include "mozilla/Unused.h"
50 #if defined(XP_WIN)
51 # include "mozilla/WinDllServices.h"
52 #endif
53 #include "nsAppDirectoryServiceDefs.h"
54 #include "nsBaseHashtable.h"
55 #include "nsClassHashtable.h"
56 #include "nsCOMArray.h"
57 #include "nsCOMPtr.h"
58 #include "nsTHashMap.h"
59 #include "nsHashKeys.h"
60 #include "nsIDirectoryEnumerator.h"
61 #include "nsDirectoryServiceDefs.h"
62 #include "nsIFileStreams.h"
63 #include "nsIMemoryReporter.h"
64 #include "nsISeekableStream.h"
65 #include "nsITelemetry.h"
66 #if defined(XP_WIN)
67 # include "other/UntrustedModules.h"
68 #endif
69 #include "nsJSUtils.h"
70 #include "nsNativeCharsetUtils.h"
71 #include "nsNetCID.h"
72 #include "nsNetUtil.h"
73 #include "nsProxyRelease.h"
74 #include "nsReadableUtils.h"
75 #include "nsString.h"
76 #include "nsTHashtable.h"
77 #include "nsThreadUtils.h"
78 #if defined(XP_WIN)
79 # include "nsUnicharUtils.h"
80 #endif
81 #include "nsVersionComparator.h"
82 #include "nsXPCOMCIDInternal.h"
83 #include "other/CombinedStacks.h"
84 #include "other/TelemetryIOInterposeObserver.h"
85 #include "TelemetryCommon.h"
86 #include "TelemetryEvent.h"
87 #include "TelemetryHistogram.h"
88 #include "TelemetryScalar.h"
89 #include "TelemetryUserInteraction.h"
91 namespace {
93 using namespace mozilla;
94 using mozilla::dom::AutoJSAPI;
95 using mozilla::dom::Promise;
96 using mozilla::Telemetry::CombinedStacks;
97 using mozilla::Telemetry::EventExtraEntry;
98 using mozilla::Telemetry::TelemetryIOInterposeObserver;
99 using Telemetry::Common::AutoHashtable;
100 using Telemetry::Common::GetCurrentProduct;
101 using Telemetry::Common::StringHashSet;
102 using Telemetry::Common::SupportedProduct;
103 using Telemetry::Common::ToJSString;
105 // This is not a member of TelemetryImpl because we want to record I/O during
106 // startup.
107 StaticAutoPtr<TelemetryIOInterposeObserver> sTelemetryIOObserver;
109 void ClearIOReporting() {
110 if (!sTelemetryIOObserver) {
111 return;
113 IOInterposer::Unregister(IOInterposeObserver::OpAllWithStaging,
114 sTelemetryIOObserver);
115 sTelemetryIOObserver = nullptr;
118 class TelemetryImpl final : public nsITelemetry, public nsIMemoryReporter {
119 NS_DECL_THREADSAFE_ISUPPORTS
120 NS_DECL_NSITELEMETRY
121 NS_DECL_NSIMEMORYREPORTER
123 public:
124 void InitMemoryReporter();
126 static already_AddRefed<nsITelemetry> CreateTelemetryInstance();
127 static void ShutdownTelemetry();
128 static void RecordSlowStatement(const nsACString& sql,
129 const nsACString& dbName, uint32_t delay);
130 struct Stat {
131 uint32_t hitCount;
132 uint32_t totalTime;
134 struct StmtStats {
135 struct Stat mainThread;
136 struct Stat otherThreads;
138 typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType;
140 static void RecordIceCandidates(const uint32_t iceCandidateBitmask,
141 const bool success);
142 static bool CanRecordBase();
143 static bool CanRecordExtended();
144 static bool CanRecordReleaseData();
145 static bool CanRecordPrereleaseData();
147 private:
148 TelemetryImpl();
149 ~TelemetryImpl();
151 static nsCString SanitizeSQL(const nsACString& sql);
153 enum SanitizedState { Sanitized, Unsanitized };
155 static void StoreSlowSQL(const nsACString& offender, uint32_t delay,
156 SanitizedState state);
158 static bool ReflectMainThreadSQL(SlowSQLEntryType* entry, JSContext* cx,
159 JS::Handle<JSObject*> obj);
160 static bool ReflectOtherThreadsSQL(SlowSQLEntryType* entry, JSContext* cx,
161 JS::Handle<JSObject*> obj);
162 static bool ReflectSQL(const SlowSQLEntryType* entry, const Stat* stat,
163 JSContext* cx, JS::Handle<JSObject*> obj);
165 bool AddSQLInfo(JSContext* cx, JS::Handle<JSObject*> rootObj, bool mainThread,
166 bool privateSQL);
167 bool GetSQLStats(JSContext* cx, JS::MutableHandle<JS::Value> ret,
168 bool includePrivateSql);
170 void ReadLateWritesStacks(nsIFile* aProfileDir);
172 static StaticDataMutex<TelemetryImpl*> sTelemetry;
173 AutoHashtable<SlowSQLEntryType> mPrivateSQL;
174 AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
175 Mutex mHashMutex MOZ_UNANNOTATED;
176 Atomic<bool, SequentiallyConsistent> mCanRecordBase;
177 Atomic<bool, SequentiallyConsistent> mCanRecordExtended;
179 CombinedStacks
180 mLateWritesStacks; // This is collected out of the main thread.
181 bool mCachedTelemetryData;
182 uint32_t mLastShutdownTime;
183 uint32_t mFailedLockCount;
184 nsCOMArray<nsIFetchTelemetryDataCallback> mCallbacks;
185 friend class nsFetchTelemetryData;
188 MOZ_RUNINIT StaticDataMutex<TelemetryImpl*> TelemetryImpl::sTelemetry(nullptr,
189 nullptr);
191 MOZ_DEFINE_MALLOC_SIZE_OF(TelemetryMallocSizeOf)
193 NS_IMETHODIMP
194 TelemetryImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
195 nsISupports* aData, bool aAnonymize) {
196 mozilla::MallocSizeOf aMallocSizeOf = TelemetryMallocSizeOf;
198 #define COLLECT_REPORT(name, size, desc) \
199 MOZ_COLLECT_REPORT(name, KIND_HEAP, UNITS_BYTES, size, desc)
201 COLLECT_REPORT("explicit/telemetry/impl", aMallocSizeOf(this),
202 "Memory used by the Telemetry core implemenation");
204 COLLECT_REPORT(
205 "explicit/telemetry/scalar/shallow",
206 TelemetryScalar::GetMapShallowSizesOfExcludingThis(aMallocSizeOf),
207 "Memory used by the Telemetry Scalar implemenation");
209 { // Scope for mHashMutex lock
210 MutexAutoLock lock(mHashMutex);
211 COLLECT_REPORT("explicit/telemetry/PrivateSQL",
212 mPrivateSQL.SizeOfExcludingThis(aMallocSizeOf),
213 "Memory used by the PrivateSQL Telemetry");
215 COLLECT_REPORT("explicit/telemetry/SanitizedSQL",
216 mSanitizedSQL.SizeOfExcludingThis(aMallocSizeOf),
217 "Memory used by the SanitizedSQL Telemetry");
220 if (sTelemetryIOObserver) {
221 COLLECT_REPORT("explicit/telemetry/IOObserver",
222 sTelemetryIOObserver->SizeOfIncludingThis(aMallocSizeOf),
223 "Memory used by the Telemetry IO Observer");
226 COLLECT_REPORT("explicit/telemetry/LateWritesStacks",
227 mLateWritesStacks.SizeOfExcludingThis(),
228 "Memory used by the Telemetry LateWrites Stack capturer");
230 COLLECT_REPORT("explicit/telemetry/Callbacks",
231 mCallbacks.ShallowSizeOfExcludingThis(aMallocSizeOf),
232 "Memory used by the Telemetry Callbacks array (shallow)");
234 COLLECT_REPORT(
235 "explicit/telemetry/histogram/data",
236 TelemetryHistogram::GetHistogramSizesOfIncludingThis(aMallocSizeOf),
237 "Memory used by Telemetry Histogram data");
239 COLLECT_REPORT("explicit/telemetry/scalar/data",
240 TelemetryScalar::GetScalarSizesOfIncludingThis(aMallocSizeOf),
241 "Memory used by Telemetry Scalar data");
243 COLLECT_REPORT("explicit/telemetry/event/data",
244 TelemetryEvent::SizeOfIncludingThis(aMallocSizeOf),
245 "Memory used by Telemetry Event data");
247 #undef COLLECT_REPORT
249 return NS_OK;
252 void InitHistogramRecordingEnabled() {
253 TelemetryHistogram::InitHistogramRecordingEnabled();
256 using PathChar = filesystem::Path::value_type;
257 using PathCharPtr = const PathChar*;
259 static uint32_t ReadLastShutdownDuration(PathCharPtr filename) {
260 nsCOMPtr<nsIFile> file;
261 if (NS_FAILED(NS_NewPathStringLocalFile(DependentPathString(filename),
262 getter_AddRefs(file)))) {
263 return 0;
265 FILE* f;
266 if (NS_FAILED(file->OpenANSIFileDesc("r", &f)) || !f) {
267 return 0;
270 int shutdownTime;
271 int r = fscanf(f, "%d\n", &shutdownTime);
272 fclose(f);
273 if (r != 1) {
274 return 0;
277 return shutdownTime;
280 const int32_t kMaxFailedProfileLockFileSize = 10;
282 bool GetFailedLockCount(nsIInputStream* inStream, uint32_t aCount,
283 unsigned int& result) {
284 nsAutoCString bufStr;
285 nsresult rv;
286 rv = NS_ReadInputStreamToString(inStream, bufStr, aCount);
287 NS_ENSURE_SUCCESS(rv, false);
288 result = bufStr.ToInteger(&rv);
289 return NS_SUCCEEDED(rv) && result > 0;
292 nsresult GetFailedProfileLockFile(nsIFile** aFile, nsIFile* aProfileDir) {
293 NS_ENSURE_ARG_POINTER(aProfileDir);
295 nsresult rv = aProfileDir->Clone(aFile);
296 NS_ENSURE_SUCCESS(rv, rv);
298 (*aFile)->AppendNative("Telemetry.FailedProfileLocks.txt"_ns);
299 return NS_OK;
302 class nsFetchTelemetryData : public Runnable {
303 public:
304 nsFetchTelemetryData(PathCharPtr aShutdownTimeFilename,
305 nsIFile* aFailedProfileLockFile, nsIFile* aProfileDir)
306 : mozilla::Runnable("nsFetchTelemetryData"),
307 mShutdownTimeFilename(aShutdownTimeFilename),
308 mFailedProfileLockFile(aFailedProfileLockFile),
309 mProfileDir(aProfileDir) {}
311 private:
312 PathCharPtr mShutdownTimeFilename;
313 nsCOMPtr<nsIFile> mFailedProfileLockFile;
314 nsCOMPtr<nsIFile> mProfileDir;
316 public:
317 void MainThread() {
318 auto lock = TelemetryImpl::sTelemetry.Lock();
319 auto telemetry = lock.ref();
320 telemetry->mCachedTelemetryData = true;
321 for (unsigned int i = 0, n = telemetry->mCallbacks.Count(); i < n; ++i) {
322 telemetry->mCallbacks[i]->Complete();
324 telemetry->mCallbacks.Clear();
327 NS_IMETHOD Run() override {
328 uint32_t failedLockCount = 0;
329 uint32_t lastShutdownDuration = 0;
330 LoadFailedLockCount(failedLockCount);
331 lastShutdownDuration = ReadLastShutdownDuration(mShutdownTimeFilename);
333 auto lock = TelemetryImpl::sTelemetry.Lock();
334 auto telemetry = lock.ref();
335 telemetry->mFailedLockCount = failedLockCount;
336 telemetry->mLastShutdownTime = lastShutdownDuration;
337 telemetry->ReadLateWritesStacks(mProfileDir);
340 glean::browser_timings::last_shutdown.Set(lastShutdownDuration);
342 nsCOMPtr<nsIRunnable> e =
343 NewRunnableMethod("nsFetchTelemetryData::MainThread", this,
344 &nsFetchTelemetryData::MainThread);
345 NS_ENSURE_STATE(e);
346 NS_DispatchToMainThread(e);
347 return NS_OK;
350 private:
351 nsresult LoadFailedLockCount(uint32_t& failedLockCount) {
352 failedLockCount = 0;
353 int64_t fileSize = 0;
354 nsresult rv = mFailedProfileLockFile->GetFileSize(&fileSize);
355 if (NS_FAILED(rv)) {
356 return rv;
358 NS_ENSURE_TRUE(fileSize <= kMaxFailedProfileLockFileSize,
359 NS_ERROR_UNEXPECTED);
360 nsCOMPtr<nsIInputStream> inStream;
361 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream),
362 mFailedProfileLockFile, PR_RDONLY);
363 NS_ENSURE_SUCCESS(rv, rv);
364 NS_ENSURE_TRUE(GetFailedLockCount(inStream, fileSize, failedLockCount),
365 NS_ERROR_UNEXPECTED);
366 inStream->Close();
368 mFailedProfileLockFile->Remove(false);
369 return NS_OK;
373 static TimeStamp gRecordedShutdownStartTime;
374 static bool gAlreadyFreedShutdownTimeFileName = false;
375 static PathCharPtr gRecordedShutdownTimeFileName = nullptr;
377 static PathCharPtr GetShutdownTimeFileName() {
378 if (gAlreadyFreedShutdownTimeFileName) {
379 return nullptr;
382 if (!gRecordedShutdownTimeFileName) {
383 nsCOMPtr<nsIFile> mozFile;
384 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
385 if (!mozFile) return nullptr;
387 mozFile->AppendNative("Telemetry.ShutdownTime.txt"_ns);
389 gRecordedShutdownTimeFileName = NS_xstrdup(mozFile->NativePath().get());
392 return gRecordedShutdownTimeFileName;
395 NS_IMETHODIMP
396 TelemetryImpl::GetLastShutdownDuration(uint32_t* aResult) {
397 // The user must call AsyncFetchTelemetryData first. We return zero instead of
398 // reporting a failure so that the rest of telemetry can uniformly handle
399 // the read not being available yet.
400 if (!mCachedTelemetryData) {
401 *aResult = 0;
402 return NS_OK;
405 *aResult = mLastShutdownTime;
406 return NS_OK;
409 NS_IMETHODIMP
410 TelemetryImpl::GetFailedProfileLockCount(uint32_t* aResult) {
411 // The user must call AsyncFetchTelemetryData first. We return zero instead of
412 // reporting a failure so that the rest of telemetry can uniformly handle
413 // the read not being available yet.
414 if (!mCachedTelemetryData) {
415 *aResult = 0;
416 return NS_OK;
419 *aResult = mFailedLockCount;
420 return NS_OK;
423 NS_IMETHODIMP
424 TelemetryImpl::AsyncFetchTelemetryData(
425 nsIFetchTelemetryDataCallback* aCallback) {
426 // We have finished reading the data already, just call the callback.
427 if (mCachedTelemetryData) {
428 aCallback->Complete();
429 return NS_OK;
432 // We already have a read request running, just remember the callback.
433 if (mCallbacks.Count() != 0) {
434 mCallbacks.AppendObject(aCallback);
435 return NS_OK;
438 // We make this check so that GetShutdownTimeFileName() doesn't get
439 // called; calling that function without telemetry enabled violates
440 // assumptions that the write-the-shutdown-timestamp machinery makes.
441 if (!Telemetry::CanRecordExtended()) {
442 mCachedTelemetryData = true;
443 aCallback->Complete();
444 return NS_OK;
447 // Send the read to a background thread provided by the stream transport
448 // service to avoid a read in the main thread.
449 nsCOMPtr<nsIEventTarget> targetThread =
450 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
451 if (!targetThread) {
452 mCachedTelemetryData = true;
453 aCallback->Complete();
454 return NS_OK;
457 // We have to get the filename from the main thread.
458 PathCharPtr shutdownTimeFilename = GetShutdownTimeFileName();
459 if (!shutdownTimeFilename) {
460 mCachedTelemetryData = true;
461 aCallback->Complete();
462 return NS_OK;
465 nsCOMPtr<nsIFile> profileDir;
466 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
467 getter_AddRefs(profileDir));
468 if (NS_FAILED(rv)) {
469 mCachedTelemetryData = true;
470 aCallback->Complete();
471 return NS_OK;
474 nsCOMPtr<nsIFile> failedProfileLockFile;
475 rv = GetFailedProfileLockFile(getter_AddRefs(failedProfileLockFile),
476 profileDir);
477 if (NS_FAILED(rv)) {
478 mCachedTelemetryData = true;
479 aCallback->Complete();
480 return NS_OK;
483 mCallbacks.AppendObject(aCallback);
485 nsCOMPtr<nsIRunnable> event = new nsFetchTelemetryData(
486 shutdownTimeFilename, failedProfileLockFile, profileDir);
488 targetThread->Dispatch(event, NS_DISPATCH_NORMAL);
489 return NS_OK;
492 TelemetryImpl::TelemetryImpl()
493 : mHashMutex("Telemetry::mHashMutex"),
494 mCanRecordBase(false),
495 mCanRecordExtended(false),
496 mCachedTelemetryData(false),
497 mLastShutdownTime(0),
498 mFailedLockCount(0) {
499 // We expect TelemetryHistogram::InitializeGlobalState() to have been
500 // called before we get to this point.
501 MOZ_ASSERT(TelemetryHistogram::GlobalStateHasBeenInitialized());
504 TelemetryImpl::~TelemetryImpl() {
505 UnregisterWeakMemoryReporter(this);
507 // This is still racey as access to these collections is guarded using
508 // sTelemetry. We will fix this in bug 1367344.
509 MutexAutoLock hashLock(mHashMutex);
512 void TelemetryImpl::InitMemoryReporter() { RegisterWeakMemoryReporter(this); }
514 bool TelemetryImpl::ReflectSQL(const SlowSQLEntryType* entry, const Stat* stat,
515 JSContext* cx, JS::Handle<JSObject*> obj) {
516 if (stat->hitCount == 0) return true;
518 const nsACString& sql = entry->GetKey();
520 JS::Rooted<JSObject*> arrayObj(cx, JS::NewArrayObject(cx, 0));
521 if (!arrayObj) {
522 return false;
524 return (
525 JS_DefineElement(cx, arrayObj, 0, stat->hitCount, JSPROP_ENUMERATE) &&
526 JS_DefineElement(cx, arrayObj, 1, stat->totalTime, JSPROP_ENUMERATE) &&
527 JS_DefineProperty(cx, obj, sql.BeginReading(), arrayObj,
528 JSPROP_ENUMERATE));
531 bool TelemetryImpl::ReflectMainThreadSQL(SlowSQLEntryType* entry, JSContext* cx,
532 JS::Handle<JSObject*> obj) {
533 return ReflectSQL(entry, &entry->GetModifiableData()->mainThread, cx, obj);
536 bool TelemetryImpl::ReflectOtherThreadsSQL(SlowSQLEntryType* entry,
537 JSContext* cx,
538 JS::Handle<JSObject*> obj) {
539 return ReflectSQL(entry, &entry->GetModifiableData()->otherThreads, cx, obj);
542 bool TelemetryImpl::AddSQLInfo(JSContext* cx, JS::Handle<JSObject*> rootObj,
543 bool mainThread, bool privateSQL) {
544 JS::Rooted<JSObject*> statsObj(cx, JS_NewPlainObject(cx));
545 if (!statsObj) return false;
547 AutoHashtable<SlowSQLEntryType>& sqlMap =
548 (privateSQL ? mPrivateSQL : mSanitizedSQL);
549 AutoHashtable<SlowSQLEntryType>::ReflectEntryFunc reflectFunction =
550 (mainThread ? ReflectMainThreadSQL : ReflectOtherThreadsSQL);
551 if (!sqlMap.ReflectIntoJS(reflectFunction, cx, statsObj)) {
552 return false;
555 return JS_DefineProperty(cx, rootObj,
556 mainThread ? "mainThread" : "otherThreads", statsObj,
557 JSPROP_ENUMERATE);
560 NS_IMETHODIMP
561 TelemetryImpl::GetSnapshotForHistograms(const nsACString& aStoreName,
562 bool aClearStore, bool aFilterTest,
563 JSContext* aCx,
564 JS::MutableHandle<JS::Value> aResult) {
565 constexpr auto defaultStore = "main"_ns;
566 unsigned int dataset = mCanRecordExtended
567 ? nsITelemetry::DATASET_PRERELEASE_CHANNELS
568 : nsITelemetry::DATASET_ALL_CHANNELS;
569 return TelemetryHistogram::CreateHistogramSnapshots(
570 aCx, aResult, aStoreName.IsVoid() ? defaultStore : aStoreName, dataset,
571 aClearStore, aFilterTest);
574 NS_IMETHODIMP
575 TelemetryImpl::GetSnapshotForKeyedHistograms(
576 const nsACString& aStoreName, bool aClearStore, bool aFilterTest,
577 JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
578 constexpr auto defaultStore = "main"_ns;
579 unsigned int dataset = mCanRecordExtended
580 ? nsITelemetry::DATASET_PRERELEASE_CHANNELS
581 : nsITelemetry::DATASET_ALL_CHANNELS;
582 return TelemetryHistogram::GetKeyedHistogramSnapshots(
583 aCx, aResult, aStoreName.IsVoid() ? defaultStore : aStoreName, dataset,
584 aClearStore, aFilterTest);
587 NS_IMETHODIMP
588 TelemetryImpl::GetCategoricalLabels(JSContext* aCx,
589 JS::MutableHandle<JS::Value> aResult) {
590 return TelemetryHistogram::GetCategoricalHistogramLabels(aCx, aResult);
593 NS_IMETHODIMP
594 TelemetryImpl::GetSnapshotForScalars(const nsACString& aStoreName,
595 bool aClearStore, bool aFilterTest,
596 JSContext* aCx,
597 JS::MutableHandle<JS::Value> aResult) {
598 constexpr auto defaultStore = "main"_ns;
599 unsigned int dataset = mCanRecordExtended
600 ? nsITelemetry::DATASET_PRERELEASE_CHANNELS
601 : nsITelemetry::DATASET_ALL_CHANNELS;
602 return TelemetryScalar::CreateSnapshots(
603 dataset, aClearStore, aCx, 1, aResult, aFilterTest,
604 aStoreName.IsVoid() ? defaultStore : aStoreName);
607 NS_IMETHODIMP
608 TelemetryImpl::GetSnapshotForKeyedScalars(
609 const nsACString& aStoreName, bool aClearStore, bool aFilterTest,
610 JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
611 constexpr auto defaultStore = "main"_ns;
612 unsigned int dataset = mCanRecordExtended
613 ? nsITelemetry::DATASET_PRERELEASE_CHANNELS
614 : nsITelemetry::DATASET_ALL_CHANNELS;
615 return TelemetryScalar::CreateKeyedSnapshots(
616 dataset, aClearStore, aCx, 1, aResult, aFilterTest,
617 aStoreName.IsVoid() ? defaultStore : aStoreName);
620 bool TelemetryImpl::GetSQLStats(JSContext* cx, JS::MutableHandle<JS::Value> ret,
621 bool includePrivateSql) {
622 JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
623 if (!root_obj) return false;
624 ret.setObject(*root_obj);
626 MutexAutoLock hashMutex(mHashMutex);
627 // Add info about slow SQL queries on the main thread
628 if (!AddSQLInfo(cx, root_obj, true, includePrivateSql)) return false;
629 // Add info about slow SQL queries on other threads
630 if (!AddSQLInfo(cx, root_obj, false, includePrivateSql)) return false;
632 return true;
635 NS_IMETHODIMP
636 TelemetryImpl::GetSlowSQL(JSContext* cx, JS::MutableHandle<JS::Value> ret) {
637 if (GetSQLStats(cx, ret, false)) return NS_OK;
638 return NS_ERROR_FAILURE;
641 NS_IMETHODIMP
642 TelemetryImpl::GetDebugSlowSQL(JSContext* cx,
643 JS::MutableHandle<JS::Value> ret) {
644 bool revealPrivateSql =
645 Preferences::GetBool("toolkit.telemetry.debugSlowSql", false);
646 if (GetSQLStats(cx, ret, revealPrivateSql)) return NS_OK;
647 return NS_ERROR_FAILURE;
650 NS_IMETHODIMP
651 TelemetryImpl::GetUntrustedModuleLoadEvents(uint32_t aFlags, JSContext* cx,
652 Promise** aPromise) {
653 #if defined(XP_WIN)
654 return Telemetry::GetUntrustedModuleLoadEvents(aFlags, cx, aPromise);
655 #else
656 return NS_ERROR_NOT_IMPLEMENTED;
657 #endif
660 NS_IMETHODIMP
661 TelemetryImpl::GetAreUntrustedModuleLoadEventsReady(bool* ret) {
662 #if defined(XP_WIN)
663 *ret = DllServices::Get()->IsReadyForBackgroundProcessing();
664 return NS_OK;
665 #else
666 return NS_ERROR_NOT_IMPLEMENTED;
667 #endif
670 #if defined(MOZ_GECKO_PROFILER)
671 class GetLoadedModulesResultRunnable final : public Runnable {
672 nsMainThreadPtrHandle<Promise> mPromise;
673 SharedLibraryInfo mRawModules;
674 nsCOMPtr<nsIThread> mWorkerThread;
675 # if defined(XP_WIN)
676 nsTHashMap<nsStringHashKey, nsString> mCertSubjects;
677 # endif // defined(XP_WIN)
679 public:
680 GetLoadedModulesResultRunnable(const nsMainThreadPtrHandle<Promise>& aPromise,
681 const SharedLibraryInfo& rawModules)
682 : mozilla::Runnable("GetLoadedModulesResultRunnable"),
683 mPromise(aPromise),
684 mRawModules(rawModules),
685 mWorkerThread(do_GetCurrentThread()) {
686 MOZ_ASSERT(!NS_IsMainThread());
687 # if defined(XP_WIN)
688 ObtainCertSubjects();
689 # endif // defined(XP_WIN)
692 NS_IMETHOD
693 Run() override {
694 MOZ_ASSERT(NS_IsMainThread());
696 mWorkerThread->Shutdown();
698 AutoJSAPI jsapi;
699 if (NS_WARN_IF(!jsapi.Init(mPromise->GetGlobalObject()))) {
700 mPromise->MaybeReject(NS_ERROR_FAILURE);
701 return NS_OK;
704 JSContext* cx = jsapi.cx();
706 JS::Rooted<JSObject*> moduleArray(cx, JS::NewArrayObject(cx, 0));
707 if (!moduleArray) {
708 mPromise->MaybeReject(NS_ERROR_FAILURE);
709 return NS_OK;
712 for (unsigned int i = 0, n = mRawModules.GetSize(); i != n; i++) {
713 const SharedLibrary& info = mRawModules.GetEntry(i);
715 JS::Rooted<JSObject*> moduleObj(cx, JS_NewPlainObject(cx));
716 if (!moduleObj) {
717 mPromise->MaybeReject(NS_ERROR_FAILURE);
718 return NS_OK;
721 // Module name.
722 JS::Rooted<JSString*> moduleName(
724 JS_NewUCStringCopyZ(
725 cx, NS_ConvertUTF8toUTF16(info.GetModuleName().c_str()).get()));
726 if (!moduleName || !JS_DefineProperty(cx, moduleObj, "name", moduleName,
727 JSPROP_ENUMERATE)) {
728 mPromise->MaybeReject(NS_ERROR_FAILURE);
729 return NS_OK;
732 // Module debug name.
733 JS::Rooted<JS::Value> moduleDebugName(cx);
735 if (!info.GetDebugName().empty()) {
736 JS::Rooted<JSString*> str_moduleDebugName(
738 JS_NewUCStringCopyZ(
739 cx, NS_ConvertUTF8toUTF16(info.GetDebugName().c_str()).get()));
740 if (!str_moduleDebugName) {
741 mPromise->MaybeReject(NS_ERROR_FAILURE);
742 return NS_OK;
744 moduleDebugName.setString(str_moduleDebugName);
745 } else {
746 moduleDebugName.setNull();
749 if (!JS_DefineProperty(cx, moduleObj, "debugName", moduleDebugName,
750 JSPROP_ENUMERATE)) {
751 mPromise->MaybeReject(NS_ERROR_FAILURE);
752 return NS_OK;
755 // Module Breakpad identifier.
756 JS::Rooted<JS::Value> id(cx);
758 if (!info.GetBreakpadId().empty()) {
759 JS::Rooted<JSString*> str_id(
760 cx, JS_NewStringCopyZ(cx, info.GetBreakpadId().c_str()));
761 if (!str_id) {
762 mPromise->MaybeReject(NS_ERROR_FAILURE);
763 return NS_OK;
765 id.setString(str_id);
766 } else {
767 id.setNull();
770 if (!JS_DefineProperty(cx, moduleObj, "debugID", id, JSPROP_ENUMERATE)) {
771 mPromise->MaybeReject(NS_ERROR_FAILURE);
772 return NS_OK;
775 // Module version.
776 JS::Rooted<JS::Value> version(cx);
778 if (!info.GetVersion().empty()) {
779 JS::Rooted<JSString*> v(
780 cx, JS_NewStringCopyZ(cx, info.GetVersion().c_str()));
781 if (!v) {
782 mPromise->MaybeReject(NS_ERROR_FAILURE);
783 return NS_OK;
785 version.setString(v);
786 } else {
787 version.setNull();
790 if (!JS_DefineProperty(cx, moduleObj, "version", version,
791 JSPROP_ENUMERATE)) {
792 mPromise->MaybeReject(NS_ERROR_FAILURE);
793 return NS_OK;
796 # if defined(XP_WIN)
797 // Cert Subject.
798 if (auto subject = mCertSubjects.Lookup(
799 NS_ConvertUTF8toUTF16(info.GetModulePath().c_str()))) {
800 JS::Rooted<JSString*> jsOrg(cx, ToJSString(cx, *subject));
801 if (!jsOrg) {
802 mPromise->MaybeReject(NS_ERROR_FAILURE);
803 return NS_OK;
806 JS::Rooted<JS::Value> certSubject(cx);
807 certSubject.setString(jsOrg);
809 if (!JS_DefineProperty(cx, moduleObj, "certSubject", certSubject,
810 JSPROP_ENUMERATE)) {
811 mPromise->MaybeReject(NS_ERROR_FAILURE);
812 return NS_OK;
815 # endif // defined(XP_WIN)
817 if (!JS_DefineElement(cx, moduleArray, i, moduleObj, JSPROP_ENUMERATE)) {
818 mPromise->MaybeReject(NS_ERROR_FAILURE);
819 return NS_OK;
823 mPromise->MaybeResolve(moduleArray);
824 return NS_OK;
827 private:
828 # if defined(XP_WIN)
829 void ObtainCertSubjects() {
830 MOZ_ASSERT(!NS_IsMainThread());
832 // NB: Currently we cannot lower this down to the profiler layer due to
833 // differing startup dependencies between the profiler and DllServices.
834 RefPtr<DllServices> dllSvc(DllServices::Get());
836 for (unsigned int i = 0, n = mRawModules.GetSize(); i != n; i++) {
837 const SharedLibrary& info = mRawModules.GetEntry(i);
839 auto orgName = dllSvc->GetBinaryOrgName(
840 NS_ConvertUTF8toUTF16(info.GetModulePath().c_str()).get());
841 if (orgName) {
842 mCertSubjects.InsertOrUpdate(
843 NS_ConvertUTF8toUTF16(info.GetModulePath().c_str()),
844 nsDependentString(orgName.get()));
848 # endif // defined(XP_WIN)
851 class GetLoadedModulesRunnable final : public Runnable {
852 nsMainThreadPtrHandle<Promise> mPromise;
854 public:
855 explicit GetLoadedModulesRunnable(
856 const nsMainThreadPtrHandle<Promise>& aPromise)
857 : mozilla::Runnable("GetLoadedModulesRunnable"), mPromise(aPromise) {}
859 NS_IMETHOD
860 Run() override {
861 nsCOMPtr<nsIRunnable> resultRunnable = new GetLoadedModulesResultRunnable(
862 mPromise, SharedLibraryInfo::GetInfoForSelf());
863 return NS_DispatchToMainThread(resultRunnable);
866 #endif // MOZ_GECKO_PROFILER
868 NS_IMETHODIMP
869 TelemetryImpl::GetLoadedModules(JSContext* cx, Promise** aPromise) {
870 #if defined(MOZ_GECKO_PROFILER)
871 nsIGlobalObject* global = xpc::CurrentNativeGlobal(cx);
872 if (NS_WARN_IF(!global)) {
873 return NS_ERROR_FAILURE;
876 ErrorResult result;
877 RefPtr<Promise> promise = Promise::Create(global, result);
878 if (NS_WARN_IF(result.Failed())) {
879 return result.StealNSResult();
882 nsCOMPtr<nsIThread> getModulesThread;
883 nsresult rv =
884 NS_NewNamedThread("TelemetryModule", getter_AddRefs(getModulesThread));
885 if (NS_WARN_IF(NS_FAILED(rv))) {
886 promise->MaybeReject(NS_ERROR_FAILURE);
887 return NS_OK;
890 nsMainThreadPtrHandle<Promise> mainThreadPromise(
891 new nsMainThreadPtrHolder<Promise>(
892 "TelemetryImpl::GetLoadedModules::Promise", promise));
893 nsCOMPtr<nsIRunnable> runnable =
894 new GetLoadedModulesRunnable(mainThreadPromise);
895 promise.forget(aPromise);
897 return getModulesThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
898 #else // MOZ_GECKO_PROFILER
899 return NS_ERROR_NOT_IMPLEMENTED;
900 #endif // MOZ_GECKO_PROFILER
903 static bool IsValidBreakpadId(const std::string& breakpadId) {
904 if (breakpadId.size() < 33) {
905 return false;
907 for (char c : breakpadId) {
908 if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
909 return false;
912 return true;
915 // Read a stack from the given file name. In case of any error, aStack is
916 // unchanged.
917 static void ReadStack(PathCharPtr aFileName,
918 Telemetry::ProcessedStack& aStack) {
919 IFStream file(aFileName);
921 size_t numModules;
922 file >> numModules;
923 if (file.fail()) {
924 return;
927 char newline = file.get();
928 if (file.fail() || newline != '\n') {
929 return;
932 Telemetry::ProcessedStack stack;
933 for (size_t i = 0; i < numModules; ++i) {
934 std::string breakpadId;
935 file >> breakpadId;
936 if (file.fail() || !IsValidBreakpadId(breakpadId)) {
937 return;
940 char space = file.get();
941 if (file.fail() || space != ' ') {
942 return;
945 std::string moduleName;
946 getline(file, moduleName);
947 if (file.fail() || moduleName[0] == ' ') {
948 return;
951 Telemetry::ProcessedStack::Module module = {
952 NS_ConvertUTF8toUTF16(moduleName.c_str()),
953 nsCString(breakpadId.c_str(), breakpadId.size()),
955 stack.AddModule(module);
958 size_t numFrames;
959 file >> numFrames;
960 if (file.fail()) {
961 return;
964 newline = file.get();
965 if (file.fail() || newline != '\n') {
966 return;
969 for (size_t i = 0; i < numFrames; ++i) {
970 uint16_t index;
971 file >> index;
972 uintptr_t offset;
973 file >> std::hex >> offset >> std::dec;
974 if (file.fail()) {
975 return;
978 Telemetry::ProcessedStack::Frame frame = {offset, index};
979 stack.AddFrame(frame);
982 aStack = stack;
985 void TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir) {
986 nsCOMPtr<nsIDirectoryEnumerator> files;
987 if (NS_FAILED(aProfileDir->GetDirectoryEntries(getter_AddRefs(files)))) {
988 return;
991 constexpr auto prefix = u"Telemetry.LateWriteFinal-"_ns;
992 nsCOMPtr<nsIFile> file;
993 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file) {
994 nsAutoString leafName;
995 if (NS_FAILED(file->GetLeafName(leafName)) ||
996 !StringBeginsWith(leafName, prefix)) {
997 continue;
1000 Telemetry::ProcessedStack stack;
1001 ReadStack(file->NativePath().get(), stack);
1002 if (stack.GetStackSize() != 0) {
1003 mLateWritesStacks.AddStack(stack);
1005 // Delete the file so that we don't report it again on the next run.
1006 file->Remove(false);
1010 NS_IMETHODIMP
1011 TelemetryImpl::GetLateWrites(JSContext* cx, JS::MutableHandle<JS::Value> ret) {
1012 // The user must call AsyncReadTelemetryData first. We return an empty list
1013 // instead of reporting a failure so that the rest of telemetry can uniformly
1014 // handle the read not being available yet.
1016 // FIXME: we allocate the js object again and again in the getter. We should
1017 // figure out a way to cache it. In order to do that we have to call
1018 // JS_AddNamedObjectRoot. A natural place to do so is in the TelemetryImpl
1019 // constructor, but it is not clear how to get a JSContext in there.
1020 // Another option would be to call it in here when we first call
1021 // CreateJSStackObject, but we would still need to figure out where to call
1022 // JS_RemoveObjectRoot. Would it be ok to never call JS_RemoveObjectRoot
1023 // and just set the pointer to nullptr is the telemetry destructor?
1025 JSObject* report;
1026 if (!mCachedTelemetryData) {
1027 CombinedStacks empty;
1028 report = CreateJSStackObject(cx, empty);
1029 } else {
1030 report = CreateJSStackObject(cx, mLateWritesStacks);
1033 if (report == nullptr) {
1034 return NS_ERROR_FAILURE;
1037 ret.setObject(*report);
1038 return NS_OK;
1041 NS_IMETHODIMP
1042 TelemetryImpl::GetHistogramById(const nsACString& name, JSContext* cx,
1043 JS::MutableHandle<JS::Value> ret) {
1044 return TelemetryHistogram::GetHistogramById(name, cx, ret);
1047 NS_IMETHODIMP
1048 TelemetryImpl::GetKeyedHistogramById(const nsACString& name, JSContext* cx,
1049 JS::MutableHandle<JS::Value> ret) {
1050 return TelemetryHistogram::GetKeyedHistogramById(name, cx, ret);
1054 * Indicates if Telemetry can record base data (FHR data). This is true if the
1055 * FHR data reporting service or self-support are enabled.
1057 * In the unlikely event that adding a new base probe is needed, please check
1058 * the data collection wiki at https://wiki.mozilla.org/Firefox/Data_Collection
1059 * and talk to the Telemetry team.
1061 NS_IMETHODIMP
1062 TelemetryImpl::GetCanRecordBase(bool* ret) {
1063 *ret = mCanRecordBase;
1064 return NS_OK;
1067 NS_IMETHODIMP
1068 TelemetryImpl::SetCanRecordBase(bool canRecord) {
1069 #ifndef FUZZING
1070 if (canRecord != mCanRecordBase) {
1071 TelemetryHistogram::SetCanRecordBase(canRecord);
1072 TelemetryScalar::SetCanRecordBase(canRecord);
1073 TelemetryEvent::SetCanRecordBase(canRecord);
1074 mCanRecordBase = canRecord;
1076 #endif
1077 return NS_OK;
1081 * Indicates if Telemetry is allowed to record extended data. Returns false if
1082 * the user hasn't opted into "extended Telemetry" on the Release channel, when
1083 * the user has explicitly opted out of Telemetry on Nightly/Aurora/Beta or if
1084 * manually set to false during tests. If the returned value is false, gathering
1085 * of extended telemetry statistics is disabled.
1087 NS_IMETHODIMP
1088 TelemetryImpl::GetCanRecordExtended(bool* ret) {
1089 *ret = mCanRecordExtended;
1090 return NS_OK;
1093 NS_IMETHODIMP
1094 TelemetryImpl::SetCanRecordExtended(bool canRecord) {
1095 #ifndef FUZZING
1096 if (canRecord != mCanRecordExtended) {
1097 TelemetryHistogram::SetCanRecordExtended(canRecord);
1098 TelemetryScalar::SetCanRecordExtended(canRecord);
1099 TelemetryEvent::SetCanRecordExtended(canRecord);
1100 mCanRecordExtended = canRecord;
1102 #endif
1103 return NS_OK;
1106 NS_IMETHODIMP
1107 TelemetryImpl::GetCanRecordReleaseData(bool* ret) {
1108 *ret = mCanRecordBase;
1109 return NS_OK;
1112 NS_IMETHODIMP
1113 TelemetryImpl::GetCanRecordPrereleaseData(bool* ret) {
1114 *ret = mCanRecordExtended;
1115 return NS_OK;
1118 NS_IMETHODIMP
1119 TelemetryImpl::GetIsOfficialTelemetry(bool* ret) {
1120 #if defined(MOZILLA_OFFICIAL) && defined(MOZ_TELEMETRY_REPORTING) && \
1121 !defined(DEBUG)
1122 *ret = true;
1123 #else
1124 *ret = false;
1125 #endif
1126 return NS_OK;
1129 already_AddRefed<nsITelemetry> TelemetryImpl::CreateTelemetryInstance() {
1131 auto lock = sTelemetry.Lock();
1132 MOZ_ASSERT(
1133 *lock == nullptr,
1134 "CreateTelemetryInstance may only be called once, via GetService()");
1137 bool useTelemetry = false;
1138 #ifndef FUZZING
1139 if (XRE_IsParentProcess() || XRE_IsContentProcess() || XRE_IsGPUProcess() ||
1140 XRE_IsRDDProcess() || XRE_IsSocketProcess() || XRE_IsUtilityProcess()) {
1141 useTelemetry = true;
1143 #endif
1144 #ifdef MOZ_BACKGROUNDTASKS
1145 if (BackgroundTasks::IsBackgroundTaskMode()) {
1146 // Background tasks collect per-task metrics with Glean.
1147 useTelemetry = false;
1149 #endif
1151 // First, initialize the TelemetryHistogram and TelemetryScalar global states.
1152 TelemetryHistogram::InitializeGlobalState(useTelemetry, useTelemetry);
1153 TelemetryScalar::InitializeGlobalState(useTelemetry, useTelemetry);
1155 // Only record events from the parent process.
1156 TelemetryEvent::InitializeGlobalState(XRE_IsParentProcess(),
1157 XRE_IsParentProcess());
1159 // Currently, only UserInteractions from the parent process are recorded.
1160 TelemetryUserInteraction::InitializeGlobalState(useTelemetry);
1162 // Now, create and initialize the Telemetry global state.
1163 TelemetryImpl* telemetry = new TelemetryImpl();
1165 auto lock = sTelemetry.Lock();
1166 *lock = telemetry;
1167 // AddRef for the local reference before releasing the lock.
1168 NS_ADDREF(telemetry);
1171 // AddRef for the caller
1172 nsCOMPtr<nsITelemetry> ret = telemetry;
1174 telemetry->mCanRecordBase = useTelemetry;
1175 telemetry->mCanRecordExtended = useTelemetry;
1177 telemetry->InitMemoryReporter();
1178 InitHistogramRecordingEnabled(); // requires sTelemetry to exist
1180 return ret.forget();
1183 void TelemetryImpl::ShutdownTelemetry() {
1184 // No point in collecting IO beyond this point
1185 ClearIOReporting();
1187 auto lock = sTelemetry.Lock();
1188 NS_IF_RELEASE(lock.ref());
1191 // Lastly, de-initialise the TelemetryHistogram and TelemetryScalar global
1192 // states, so as to release any heap storage that would otherwise be kept
1193 // alive by it.
1194 TelemetryHistogram::DeInitializeGlobalState();
1195 TelemetryScalar::DeInitializeGlobalState();
1196 TelemetryEvent::DeInitializeGlobalState();
1198 TelemetryUserInteraction::DeInitializeGlobalState();
1199 TelemetryIPCAccumulator::DeInitializeGlobalState();
1202 void TelemetryImpl::StoreSlowSQL(const nsACString& sql, uint32_t delay,
1203 SanitizedState state) {
1204 auto lock = sTelemetry.Lock();
1205 auto telemetry = lock.ref();
1206 AutoHashtable<SlowSQLEntryType>* slowSQLMap = nullptr;
1207 if (state == Sanitized)
1208 slowSQLMap = &(telemetry->mSanitizedSQL);
1209 else
1210 slowSQLMap = &(telemetry->mPrivateSQL);
1212 MutexAutoLock hashMutex(telemetry->mHashMutex);
1214 SlowSQLEntryType* entry = slowSQLMap->GetEntry(sql);
1215 if (!entry) {
1216 entry = slowSQLMap->PutEntry(sql);
1217 if (MOZ_UNLIKELY(!entry)) return;
1218 entry->GetModifiableData()->mainThread.hitCount = 0;
1219 entry->GetModifiableData()->mainThread.totalTime = 0;
1220 entry->GetModifiableData()->otherThreads.hitCount = 0;
1221 entry->GetModifiableData()->otherThreads.totalTime = 0;
1224 if (NS_IsMainThread()) {
1225 entry->GetModifiableData()->mainThread.hitCount++;
1226 entry->GetModifiableData()->mainThread.totalTime += delay;
1227 } else {
1228 entry->GetModifiableData()->otherThreads.hitCount++;
1229 entry->GetModifiableData()->otherThreads.totalTime += delay;
1234 * This method replaces string literals in SQL strings with the word :private
1236 * States used in this state machine:
1238 * NORMAL:
1239 * - This is the active state when not iterating over a string literal or
1240 * comment
1242 * SINGLE_QUOTE:
1243 * - Defined here: http://www.sqlite.org/lang_expr.html
1244 * - This state represents iterating over a string literal opened with
1245 * a single quote.
1246 * - A single quote within the string can be encoded by putting 2 single quotes
1247 * in a row, e.g. 'This literal contains an escaped quote '''
1248 * - Any double quotes found within a single-quoted literal are ignored
1249 * - This state covers BLOB literals, e.g. X'ABC123'
1250 * - The string literal and the enclosing quotes will be replaced with
1251 * the text :private
1253 * DOUBLE_QUOTE:
1254 * - Same rules as the SINGLE_QUOTE state.
1255 * - According to http://www.sqlite.org/lang_keywords.html,
1256 * SQLite interprets text in double quotes as an identifier unless it's used in
1257 * a context where it cannot be resolved to an identifier and a string literal
1258 * is allowed. This method removes text in double-quotes for safety.
1260 * DASH_COMMENT:
1261 * - http://www.sqlite.org/lang_comment.html
1262 * - A dash comment starts with two dashes in a row,
1263 * e.g. DROP TABLE foo -- a comment
1264 * - Any text following two dashes in a row is interpreted as a comment until
1265 * end of input or a newline character
1266 * - Any quotes found within the comment are ignored and no replacements made
1268 * C_STYLE_COMMENT:
1269 * - http://www.sqlite.org/lang_comment.html
1270 * - A C-style comment starts with a forward slash and an asterisk, and ends
1271 * with an asterisk and a forward slash
1272 * - Any text following comment start is interpreted as a comment up to end of
1273 * input or comment end
1274 * - Any quotes found within the comment are ignored and no replacements made
1276 nsCString TelemetryImpl::SanitizeSQL(const nsACString& sql) {
1277 nsCString output;
1278 int length = sql.Length();
1280 typedef enum {
1281 NORMAL,
1282 SINGLE_QUOTE,
1283 DOUBLE_QUOTE,
1284 DASH_COMMENT,
1285 C_STYLE_COMMENT,
1286 } State;
1288 State state = NORMAL;
1289 int fragmentStart = 0;
1290 for (int i = 0; i < length; i++) {
1291 char character = sql[i];
1292 char nextCharacter = (i + 1 < length) ? sql[i + 1] : '\0';
1294 switch (character) {
1295 case '\'':
1296 case '"':
1297 if (state == NORMAL) {
1298 state = (character == '\'') ? SINGLE_QUOTE : DOUBLE_QUOTE;
1299 output +=
1300 nsDependentCSubstring(sql, fragmentStart, i - fragmentStart);
1301 output += ":private";
1302 fragmentStart = -1;
1303 } else if ((state == SINGLE_QUOTE && character == '\'') ||
1304 (state == DOUBLE_QUOTE && character == '"')) {
1305 if (nextCharacter == character) {
1306 // Two consecutive quotes within a string literal are a single
1307 // escaped quote
1308 i++;
1309 } else {
1310 state = NORMAL;
1311 fragmentStart = i + 1;
1314 break;
1315 case '-':
1316 if (state == NORMAL) {
1317 if (nextCharacter == '-') {
1318 state = DASH_COMMENT;
1319 i++;
1322 break;
1323 case '\n':
1324 if (state == DASH_COMMENT) {
1325 state = NORMAL;
1327 break;
1328 case '/':
1329 if (state == NORMAL) {
1330 if (nextCharacter == '*') {
1331 state = C_STYLE_COMMENT;
1332 i++;
1335 break;
1336 case '*':
1337 if (state == C_STYLE_COMMENT) {
1338 if (nextCharacter == '/') {
1339 state = NORMAL;
1342 break;
1343 default:
1344 continue;
1348 if ((fragmentStart >= 0) && fragmentStart < length)
1349 output += nsDependentCSubstring(sql, fragmentStart, length - fragmentStart);
1351 return output;
1354 // An allowlist mechanism to prevent Telemetry reporting on Addon & Thunderbird
1355 // DBs.
1356 struct TrackedDBEntry {
1357 const char* mName;
1358 const uint32_t mNameLength;
1360 // This struct isn't meant to be used beyond the static arrays below.
1361 constexpr TrackedDBEntry(const char* aName, uint32_t aNameLength)
1362 : mName(aName), mNameLength(aNameLength) {}
1364 TrackedDBEntry() = delete;
1365 TrackedDBEntry(TrackedDBEntry&) = delete;
1368 #define TRACKEDDB_ENTRY(_name) {_name, (sizeof(_name) - 1)}
1370 // An allowlist of database names. If the database name exactly matches one of
1371 // these then its SQL statements will always be recorded.
1372 static constexpr TrackedDBEntry kTrackedDBs[] = {
1373 // IndexedDB for about:home, see aboutHome.js
1374 TRACKEDDB_ENTRY("818200132aebmoouht.sqlite"),
1375 TRACKEDDB_ENTRY("addons.sqlite"),
1376 TRACKEDDB_ENTRY("content-prefs.sqlite"),
1377 TRACKEDDB_ENTRY("cookies.sqlite"),
1378 TRACKEDDB_ENTRY("extensions.sqlite"),
1379 TRACKEDDB_ENTRY("favicons.sqlite"),
1380 TRACKEDDB_ENTRY("formhistory.sqlite"),
1381 TRACKEDDB_ENTRY("index.sqlite"),
1382 TRACKEDDB_ENTRY("netpredictions.sqlite"),
1383 TRACKEDDB_ENTRY("permissions.sqlite"),
1384 TRACKEDDB_ENTRY("places.sqlite"),
1385 TRACKEDDB_ENTRY("reading-list.sqlite"),
1386 TRACKEDDB_ENTRY("search.sqlite"),
1387 TRACKEDDB_ENTRY("urlclassifier3.sqlite"),
1388 TRACKEDDB_ENTRY("webappsstore.sqlite")};
1390 // An allowlist of database name prefixes. If the database name begins with
1391 // one of these prefixes then its SQL statements will always be recorded.
1392 static const TrackedDBEntry kTrackedDBPrefixes[] = {
1393 TRACKEDDB_ENTRY("indexedDB-")};
1395 #undef TRACKEDDB_ENTRY
1397 // Slow SQL statements will be automatically
1398 // trimmed to kMaxSlowStatementLength characters.
1399 // This limit doesn't include the ellipsis and DB name,
1400 // that are appended at the end of the stored statement.
1401 const uint32_t kMaxSlowStatementLength = 1000;
1403 void TelemetryImpl::RecordSlowStatement(const nsACString& sql,
1404 const nsACString& dbName,
1405 uint32_t delay) {
1406 MOZ_ASSERT(!sql.IsEmpty());
1407 MOZ_ASSERT(!dbName.IsEmpty());
1410 auto lock = sTelemetry.Lock();
1411 if (!lock.ref() || !TelemetryHistogram::CanRecordExtended()) {
1412 return;
1416 bool recordStatement = false;
1418 for (const TrackedDBEntry& nameEntry : kTrackedDBs) {
1419 MOZ_ASSERT(nameEntry.mNameLength);
1420 const nsDependentCString name(nameEntry.mName, nameEntry.mNameLength);
1421 if (dbName == name) {
1422 recordStatement = true;
1423 break;
1427 if (!recordStatement) {
1428 for (const TrackedDBEntry& prefixEntry : kTrackedDBPrefixes) {
1429 MOZ_ASSERT(prefixEntry.mNameLength);
1430 const nsDependentCString prefix(prefixEntry.mName,
1431 prefixEntry.mNameLength);
1432 if (StringBeginsWith(dbName, prefix)) {
1433 recordStatement = true;
1434 break;
1439 if (recordStatement) {
1440 nsAutoCString sanitizedSQL(SanitizeSQL(sql));
1441 if (sanitizedSQL.Length() > kMaxSlowStatementLength) {
1442 sanitizedSQL.SetLength(kMaxSlowStatementLength);
1443 sanitizedSQL += "...";
1445 sanitizedSQL.AppendPrintf(" /* %s */", nsPromiseFlatCString(dbName).get());
1446 StoreSlowSQL(sanitizedSQL, delay, Sanitized);
1447 } else {
1448 // Report aggregate DB-level statistics for addon DBs
1449 nsAutoCString aggregate;
1450 aggregate.AppendPrintf("Untracked SQL for %s",
1451 nsPromiseFlatCString(dbName).get());
1452 StoreSlowSQL(aggregate, delay, Sanitized);
1455 nsAutoCString fullSQL;
1456 fullSQL.AppendPrintf("%s /* %s */", nsPromiseFlatCString(sql).get(),
1457 nsPromiseFlatCString(dbName).get());
1458 StoreSlowSQL(fullSQL, delay, Unsanitized);
1461 bool TelemetryImpl::CanRecordBase() {
1462 auto lock = sTelemetry.Lock();
1463 auto telemetry = lock.ref();
1464 if (!telemetry) {
1465 return false;
1467 bool canRecordBase;
1468 nsresult rv = telemetry->GetCanRecordBase(&canRecordBase);
1469 return NS_SUCCEEDED(rv) && canRecordBase;
1472 bool TelemetryImpl::CanRecordExtended() {
1473 auto lock = sTelemetry.Lock();
1474 auto telemetry = lock.ref();
1475 if (!telemetry) {
1476 return false;
1478 bool canRecordExtended;
1479 nsresult rv = telemetry->GetCanRecordExtended(&canRecordExtended);
1480 return NS_SUCCEEDED(rv) && canRecordExtended;
1483 bool TelemetryImpl::CanRecordReleaseData() { return CanRecordBase(); }
1485 bool TelemetryImpl::CanRecordPrereleaseData() { return CanRecordExtended(); }
1487 NS_IMPL_ISUPPORTS(TelemetryImpl, nsITelemetry, nsIMemoryReporter)
1489 NS_IMETHODIMP
1490 TelemetryImpl::GetFileIOReports(JSContext* cx,
1491 JS::MutableHandle<JS::Value> ret) {
1492 if (sTelemetryIOObserver) {
1493 JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
1494 if (!obj) {
1495 return NS_ERROR_FAILURE;
1498 if (!sTelemetryIOObserver->ReflectIntoJS(cx, obj)) {
1499 return NS_ERROR_FAILURE;
1501 ret.setObject(*obj);
1502 return NS_OK;
1504 ret.setNull();
1505 return NS_OK;
1508 NS_IMETHODIMP
1509 TelemetryImpl::MsSinceProcessStart(double* aResult) {
1510 return Telemetry::Common::MsSinceProcessStart(aResult);
1513 NS_IMETHODIMP
1514 TelemetryImpl::MsSinceProcessStartIncludingSuspend(double* aResult) {
1515 return Telemetry::Common::MsSinceProcessStartIncludingSuspend(aResult);
1518 NS_IMETHODIMP
1519 TelemetryImpl::MsSinceProcessStartExcludingSuspend(double* aResult) {
1520 return Telemetry::Common::MsSinceProcessStartExcludingSuspend(aResult);
1523 NS_IMETHODIMP
1524 TelemetryImpl::MsSystemNow(double* aResult) {
1525 #if defined(XP_UNIX) && !defined(XP_DARWIN)
1526 timespec ts;
1527 clock_gettime(CLOCK_REALTIME, &ts);
1528 *aResult = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
1529 #else
1530 using namespace std::chrono;
1531 milliseconds ms =
1532 duration_cast<milliseconds>(system_clock::now().time_since_epoch());
1533 *aResult = static_cast<double>(ms.count());
1534 #endif // XP_UNIX && !XP_DARWIN
1536 return NS_OK;
1539 // Telemetry Scalars IDL Implementation
1541 NS_IMETHODIMP
1542 TelemetryImpl::RegisterBuiltinScalars(const nsACString& aCategoryName,
1543 JS::Handle<JS::Value> aScalarData,
1544 JSContext* cx) {
1545 return TelemetryScalar::RegisterScalars(aCategoryName, aScalarData, cx);
1548 NS_IMETHODIMP
1549 TelemetryImpl::ClearScalars() {
1550 TelemetryScalar::ClearScalars();
1551 return NS_OK;
1554 // Telemetry Event IDL implementation.
1556 NS_IMETHODIMP
1557 TelemetryImpl::SnapshotEvents(uint32_t aDataset, bool aClear,
1558 uint32_t aEventLimit, JSContext* aCx,
1559 uint8_t optional_argc,
1560 JS::MutableHandle<JS::Value> aResult) {
1561 return TelemetryEvent::CreateSnapshots(aDataset, aClear, aEventLimit, aCx,
1562 optional_argc, aResult);
1565 NS_IMETHODIMP
1566 TelemetryImpl::RegisterBuiltinEvents(const nsACString& aCategory,
1567 JS::Handle<JS::Value> aEventData,
1568 JSContext* cx) {
1569 return TelemetryEvent::RegisterBuiltinEvents(aCategory, aEventData, cx);
1572 NS_IMETHODIMP
1573 TelemetryImpl::ClearEvents() {
1574 TelemetryEvent::ClearEvents();
1575 return NS_OK;
1578 NS_IMETHODIMP
1579 TelemetryImpl::FlushBatchedChildTelemetry() {
1580 TelemetryIPCAccumulator::IPCTimerFired(nullptr, nullptr);
1581 return NS_OK;
1584 NS_IMETHODIMP
1585 TelemetryImpl::EarlyInit() {
1586 Unused << MemoryTelemetry::Get();
1588 return NS_OK;
1591 NS_IMETHODIMP
1592 TelemetryImpl::DelayedInit() {
1593 MemoryTelemetry::Get().DelayedInit();
1594 return NS_OK;
1597 NS_IMETHODIMP
1598 TelemetryImpl::Shutdown() {
1599 MemoryTelemetry::Get().Shutdown();
1600 return NS_OK;
1603 NS_IMETHODIMP
1604 TelemetryImpl::GatherMemory(JSContext* aCx, Promise** aResult) {
1605 ErrorResult rv;
1606 RefPtr<Promise> promise = Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
1607 if (rv.Failed()) {
1608 return rv.StealNSResult();
1611 MemoryTelemetry::Get().GatherReports(
1612 [promise]() { promise->MaybeResolve(JS::UndefinedHandleValue); });
1614 promise.forget(aResult);
1615 return NS_OK;
1618 NS_IMETHODIMP
1619 TelemetryImpl::GetAllStores(JSContext* aCx,
1620 JS::MutableHandle<JS::Value> aResult) {
1621 StringHashSet stores;
1622 nsresult rv;
1624 rv = TelemetryHistogram::GetAllStores(stores);
1625 if (NS_FAILED(rv)) {
1626 return rv;
1628 rv = TelemetryScalar::GetAllStores(stores);
1629 if (NS_FAILED(rv)) {
1630 return rv;
1633 JS::RootedVector<JS::Value> allStores(aCx);
1634 if (!allStores.reserve(stores.Count())) {
1635 return NS_ERROR_FAILURE;
1638 for (const auto& value : stores) {
1639 JS::Rooted<JS::Value> store(aCx);
1641 store.setString(ToJSString(aCx, value));
1642 if (!allStores.append(store)) {
1643 return NS_ERROR_FAILURE;
1647 JS::Rooted<JSObject*> rarray(aCx, JS::NewArrayObject(aCx, allStores));
1648 if (rarray == nullptr) {
1649 return NS_ERROR_FAILURE;
1651 aResult.setObject(*rarray);
1653 return NS_OK;
1656 } // namespace
1658 ////////////////////////////////////////////////////////////////////////
1659 ////////////////////////////////////////////////////////////////////////
1661 // EXTERNALLY VISIBLE FUNCTIONS in no name space
1662 // These are NOT listed in Telemetry.h
1665 * The XRE_TelemetryAdd function is to be used by embedding applications
1666 * that can't use mozilla::Telemetry::Accumulate() directly.
1668 void XRE_TelemetryAccumulate(int aID, uint32_t aSample) {
1669 mozilla::Telemetry::Accumulate((mozilla::Telemetry::HistogramID)aID, aSample);
1672 ////////////////////////////////////////////////////////////////////////
1673 ////////////////////////////////////////////////////////////////////////
1675 // EXTERNALLY VISIBLE FUNCTIONS in mozilla::
1676 // These are NOT listed in Telemetry.h
1678 namespace mozilla {
1680 void RecordShutdownStartTimeStamp() {
1681 #ifdef DEBUG
1682 // FIXME: this function should only be called once, since it should be called
1683 // at the earliest point we *know* we are shutting down. Unfortunately
1684 // this assert has been firing. Given that if we are called multiple times
1685 // we just keep the last timestamp, the assert is commented for now.
1686 static bool recorded = false;
1687 // MOZ_ASSERT(!recorded);
1688 (void)
1689 recorded; // Silence unused-var warnings (remove when assert re-enabled)
1690 recorded = true;
1691 #endif
1693 if (!Telemetry::CanRecordExtended()) return;
1695 gRecordedShutdownStartTime = TimeStamp::Now();
1697 GetShutdownTimeFileName();
1700 void RecordShutdownEndTimeStamp() {
1701 if (!gRecordedShutdownTimeFileName || gAlreadyFreedShutdownTimeFileName)
1702 return;
1704 PathString name(gRecordedShutdownTimeFileName);
1705 free(const_cast<PathChar*>(gRecordedShutdownTimeFileName));
1706 gRecordedShutdownTimeFileName = nullptr;
1707 gAlreadyFreedShutdownTimeFileName = true;
1709 if (gRecordedShutdownStartTime.IsNull()) {
1710 // If |CanRecordExtended()| is true before |AsyncFetchTelemetryData| is
1711 // called and then disabled before shutdown, |RecordShutdownStartTimeStamp|
1712 // will bail out and we will end up with a null |gRecordedShutdownStartTime|
1713 // here. This can happen during tests.
1714 return;
1717 AutoPathString tmpName(name);
1718 tmpName.AppendLiteral(".tmp");
1719 nsCOMPtr<nsIFile> tmpFile;
1720 if (NS_FAILED(NS_NewPathStringLocalFile(tmpName, getter_AddRefs(tmpFile)))) {
1721 return;
1723 FILE* f;
1724 if (NS_FAILED(tmpFile->OpenANSIFileDesc("w", &f)) || !f) return;
1725 // On a normal release build this should be called just before
1726 // calling _exit, but on a debug build or when the user forces a full
1727 // shutdown this is called as late as possible, so we have to
1728 // allow this write as write poisoning will be enabled.
1729 MozillaRegisterDebugFILE(f);
1731 TimeStamp now = TimeStamp::Now();
1732 MOZ_ASSERT(now >= gRecordedShutdownStartTime);
1733 TimeDuration diff = now - gRecordedShutdownStartTime;
1734 uint32_t diff2 = diff.ToMilliseconds();
1735 int written = fprintf(f, "%d\n", diff2);
1736 MozillaUnRegisterDebugFILE(f);
1737 int rv = fclose(f);
1738 if (written < 0 || rv != 0) {
1739 tmpFile->Remove(false);
1740 return;
1742 nsCOMPtr<nsIFile> file;
1743 if (NS_FAILED(NS_NewPathStringLocalFile(name, getter_AddRefs(file)))) {
1744 return;
1746 nsAutoString leafName;
1747 if (NS_SUCCEEDED(file->GetLeafName(leafName))) {
1748 tmpFile->RenameTo(nullptr, leafName);
1752 } // namespace mozilla
1754 ////////////////////////////////////////////////////////////////////////
1755 ////////////////////////////////////////////////////////////////////////
1757 // EXTERNALLY VISIBLE FUNCTIONS in mozilla::Telemetry::
1758 // These are listed in Telemetry.h
1760 namespace mozilla::Telemetry {
1762 void Accumulate(HistogramID aHistogram, uint32_t aSample) {
1763 TelemetryHistogram::Accumulate(aHistogram, aSample);
1766 void Accumulate(HistogramID aHistogram, const nsTArray<uint32_t>& aSamples) {
1767 TelemetryHistogram::Accumulate(aHistogram, aSamples);
1770 void Accumulate(HistogramID aID, const nsCString& aKey, uint32_t aSample) {
1771 TelemetryHistogram::Accumulate(aID, aKey, aSample);
1774 void Accumulate(HistogramID aID, const nsCString& aKey,
1775 const nsTArray<uint32_t>& aSamples) {
1776 TelemetryHistogram::Accumulate(aID, aKey, aSamples);
1779 void Accumulate(const char* name, uint32_t sample) {
1780 TelemetryHistogram::Accumulate(name, sample);
1783 void Accumulate(const char* name, const nsCString& key, uint32_t sample) {
1784 TelemetryHistogram::Accumulate(name, key, sample);
1787 void AccumulateCategorical(HistogramID id, const nsCString& label) {
1788 TelemetryHistogram::AccumulateCategorical(id, label);
1791 void AccumulateCategorical(HistogramID id, const nsTArray<nsCString>& labels) {
1792 TelemetryHistogram::AccumulateCategorical(id, labels);
1795 void AccumulateTimeDelta(HistogramID aHistogram, TimeStamp start,
1796 TimeStamp end) {
1797 Accumulate(aHistogram, static_cast<uint32_t>((end - start).ToMilliseconds()));
1800 void AccumulateTimeDelta(HistogramID aHistogram, const nsCString& key,
1801 TimeStamp start, TimeStamp end) {
1802 Accumulate(aHistogram, key,
1803 static_cast<uint32_t>((end - start).ToMilliseconds()));
1805 const char* GetHistogramName(HistogramID id) {
1806 return TelemetryHistogram::GetHistogramName(id);
1809 bool CanRecordBase() { return TelemetryImpl::CanRecordBase(); }
1811 bool CanRecordExtended() { return TelemetryImpl::CanRecordExtended(); }
1813 bool CanRecordReleaseData() { return TelemetryImpl::CanRecordReleaseData(); }
1815 bool CanRecordPrereleaseData() {
1816 return TelemetryImpl::CanRecordPrereleaseData();
1819 void RecordSlowSQLStatement(const nsACString& statement,
1820 const nsACString& dbName, uint32_t delay) {
1821 TelemetryImpl::RecordSlowStatement(statement, dbName, delay);
1824 void Init() {
1825 // Make the service manager hold a long-lived reference to the service
1826 nsCOMPtr<nsITelemetry> telemetryService =
1827 do_GetService("@mozilla.org/base/telemetry;1");
1828 MOZ_ASSERT(telemetryService);
1831 void WriteFailedProfileLock(nsIFile* aProfileDir) {
1832 nsCOMPtr<nsIFile> file;
1833 nsresult rv = GetFailedProfileLockFile(getter_AddRefs(file), aProfileDir);
1834 NS_ENSURE_SUCCESS_VOID(rv);
1835 int64_t fileSize = 0;
1836 rv = file->GetFileSize(&fileSize);
1837 // It's expected that the file might not exist yet
1838 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
1839 return;
1841 nsCOMPtr<nsIRandomAccessStream> fileRandomAccessStream;
1842 rv = NS_NewLocalFileRandomAccessStream(getter_AddRefs(fileRandomAccessStream),
1843 file, PR_RDWR | PR_CREATE_FILE, 0640);
1844 NS_ENSURE_SUCCESS_VOID(rv);
1845 NS_ENSURE_TRUE_VOID(fileSize <= kMaxFailedProfileLockFileSize);
1846 unsigned int failedLockCount = 0;
1847 if (fileSize > 0) {
1848 nsCOMPtr<nsIInputStream> inStream =
1849 do_QueryInterface(fileRandomAccessStream);
1850 NS_ENSURE_TRUE_VOID(inStream);
1851 if (!GetFailedLockCount(inStream, fileSize, failedLockCount)) {
1852 failedLockCount = 0;
1855 ++failedLockCount;
1856 nsAutoCString bufStr;
1857 bufStr.AppendInt(static_cast<int>(failedLockCount));
1858 // If we read in an existing failed lock count, we need to reset the file ptr
1859 if (fileSize > 0) {
1860 rv = fileRandomAccessStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1861 NS_ENSURE_SUCCESS_VOID(rv);
1863 nsCOMPtr<nsIOutputStream> outStream =
1864 do_QueryInterface(fileRandomAccessStream);
1865 uint32_t bytesLeft = bufStr.Length();
1866 const char* bytes = bufStr.get();
1867 do {
1868 uint32_t written = 0;
1869 rv = outStream->Write(bytes, bytesLeft, &written);
1870 if (NS_FAILED(rv)) {
1871 break;
1873 bytes += written;
1874 bytesLeft -= written;
1875 } while (bytesLeft > 0);
1876 fileRandomAccessStream->SetEOF();
1879 void InitIOReporting(nsIFile* aXreDir) {
1880 // Never initialize twice
1881 if (sTelemetryIOObserver) {
1882 return;
1885 sTelemetryIOObserver = new TelemetryIOInterposeObserver(aXreDir);
1886 IOInterposer::Register(IOInterposeObserver::OpAllWithStaging,
1887 sTelemetryIOObserver);
1890 void SetProfileDir(nsIFile* aProfD) {
1891 if (!sTelemetryIOObserver || !aProfD) {
1892 return;
1894 nsAutoString profDirPath;
1895 nsresult rv = aProfD->GetPath(profDirPath);
1896 if (NS_FAILED(rv)) {
1897 return;
1899 sTelemetryIOObserver->AddPath(profDirPath, u"{profile}"_ns);
1902 void ShutdownTelemetry() { TelemetryImpl::ShutdownTelemetry(); }
1904 } // namespace mozilla::Telemetry
1906 NS_IMPL_COMPONENT_FACTORY(nsITelemetry) {
1907 return TelemetryImpl::CreateTelemetryInstance().downcast<nsISupports>();