Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include <algorithm>
8 :
9 : #include <fstream>
10 :
11 : #include <prio.h>
12 : #include <prproces.h>
13 : #ifdef XP_LINUX
14 : #include <time.h>
15 : #else
16 : #include <chrono>
17 : #endif
18 :
19 : #include "mozilla/dom/ToJSValue.h"
20 : #include "mozilla/dom/Promise.h"
21 : #include "mozilla/Atomics.h"
22 : #include "mozilla/Attributes.h"
23 : #include "mozilla/DebugOnly.h"
24 : #include "mozilla/Likely.h"
25 : #include "mozilla/MathAlgorithms.h"
26 : #include "mozilla/Unused.h"
27 :
28 : #include "base/pickle.h"
29 : #include "nsIComponentManager.h"
30 : #include "nsIServiceManager.h"
31 : #include "nsThreadManager.h"
32 : #include "nsXPCOMCIDInternal.h"
33 : #include "nsCOMArray.h"
34 : #include "nsCOMPtr.h"
35 : #include "nsXPCOMPrivate.h"
36 : #include "nsIXULAppInfo.h"
37 : #include "nsVersionComparator.h"
38 : #include "mozilla/MemoryReporting.h"
39 : #include "mozilla/ModuleUtils.h"
40 : #include "nsIXPConnect.h"
41 : #include "mozilla/Services.h"
42 : #include "jsapi.h"
43 : #include "jsfriendapi.h"
44 : #include "js/GCAPI.h"
45 : #include "nsString.h"
46 : #include "nsITelemetry.h"
47 : #include "nsIFile.h"
48 : #include "nsIFileStreams.h"
49 : #include "nsIMemoryReporter.h"
50 : #include "nsISeekableStream.h"
51 : #include "Telemetry.h"
52 : #include "TelemetryCommon.h"
53 : #include "TelemetryHistogram.h"
54 : #include "ipc/TelemetryIPCAccumulator.h"
55 : #include "TelemetryScalar.h"
56 : #include "TelemetryEvent.h"
57 : #include "WebrtcTelemetry.h"
58 : #include "nsTHashtable.h"
59 : #include "nsHashKeys.h"
60 : #include "nsBaseHashtable.h"
61 : #include "nsClassHashtable.h"
62 : #include "nsXULAppAPI.h"
63 : #include "nsReadableUtils.h"
64 : #include "nsThreadUtils.h"
65 : #if defined(XP_WIN)
66 : #include "nsUnicharUtils.h"
67 : #endif
68 : #include "nsNetCID.h"
69 : #include "nsNetUtil.h"
70 : #include "nsJSUtils.h"
71 : #include "nsReadableUtils.h"
72 : #include "plstr.h"
73 : #include "nsAppDirectoryServiceDefs.h"
74 : #include "mozilla/BackgroundHangMonitor.h"
75 : #include "mozilla/ThreadHangStats.h"
76 : #include "mozilla/ProcessedStack.h"
77 : #include "mozilla/Mutex.h"
78 : #include "mozilla/FileUtils.h"
79 : #include "mozilla/Preferences.h"
80 : #include "mozilla/StaticPtr.h"
81 : #include "mozilla/IOInterposer.h"
82 : #include "mozilla/PoisonIOInterposer.h"
83 : #include "mozilla/StartupTimeline.h"
84 : #include "mozilla/HangMonitor.h"
85 : #include "nsNativeCharsetUtils.h"
86 : #include "nsProxyRelease.h"
87 : #include "HangReports.h"
88 :
89 : #if defined(MOZ_GECKO_PROFILER)
90 : #include "shared-libraries.h"
91 : #include "KeyedStackCapturer.h"
92 : #endif // MOZ_GECKO_PROFILER
93 :
94 : namespace {
95 :
96 : using namespace mozilla;
97 : using namespace mozilla::HangMonitor;
98 : using Telemetry::Common::AutoHashtable;
99 : using mozilla::dom::Promise;
100 : using mozilla::dom::AutoJSAPI;
101 : using mozilla::Telemetry::HangReports;
102 : using mozilla::Telemetry::CombinedStacks;
103 : using mozilla::Telemetry::ComputeAnnotationsKey;
104 :
105 : #if defined(MOZ_GECKO_PROFILER)
106 : using mozilla::Telemetry::KeyedStackCapturer;
107 : #endif
108 :
109 : /**
110 : * IOInterposeObserver recording statistics of main-thread I/O during execution,
111 : * aimed at consumption by TelemetryImpl
112 : */
113 0 : class TelemetryIOInterposeObserver : public IOInterposeObserver
114 : {
115 : /** File-level statistics structure */
116 : struct FileStats {
117 0 : FileStats()
118 0 : : creates(0)
119 : , reads(0)
120 : , writes(0)
121 : , fsyncs(0)
122 : , stats(0)
123 0 : , totalTime(0)
124 0 : {}
125 : uint32_t creates; /** Number of create/open operations */
126 : uint32_t reads; /** Number of read operations */
127 : uint32_t writes; /** Number of write operations */
128 : uint32_t fsyncs; /** Number of fsync operations */
129 : uint32_t stats; /** Number of stat operations */
130 : double totalTime; /** Accumulated duration of all operations */
131 : };
132 :
133 4 : struct SafeDir {
134 2 : SafeDir(const nsAString& aPath, const nsAString& aSubstName)
135 2 : : mPath(aPath)
136 2 : , mSubstName(aSubstName)
137 2 : {}
138 0 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
139 0 : return mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
140 0 : mSubstName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
141 : }
142 : nsString mPath; /** Path to the directory */
143 : nsString mSubstName; /** Name to substitute with */
144 : };
145 :
146 : public:
147 : explicit TelemetryIOInterposeObserver(nsIFile* aXreDir);
148 :
149 : /**
150 : * An implementation of Observe that records statistics of all
151 : * file IO operations.
152 : */
153 : void Observe(Observation& aOb) override;
154 :
155 : /**
156 : * Reflect recorded file IO statistics into Javascript
157 : */
158 : bool ReflectIntoJS(JSContext *cx, JS::Handle<JSObject*> rootObj);
159 :
160 : /**
161 : * Adds a path for inclusion in main thread I/O report.
162 : * @param aPath Directory path
163 : * @param aSubstName Name to substitute for aPath for privacy reasons
164 : */
165 : void AddPath(const nsAString& aPath, const nsAString& aSubstName);
166 :
167 : /**
168 : * Get size of hash table with file stats
169 : */
170 0 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
171 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
172 : }
173 :
174 0 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
175 0 : size_t size = 0;
176 0 : size += mFileStats.ShallowSizeOfExcludingThis(aMallocSizeOf);
177 0 : for (auto iter = mFileStats.ConstIter(); !iter.Done(); iter.Next()) {
178 0 : size += iter.Get()->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
179 : }
180 0 : size += mSafeDirs.ShallowSizeOfExcludingThis(aMallocSizeOf);
181 0 : uint32_t safeDirsLen = mSafeDirs.Length();
182 0 : for (uint32_t i = 0; i < safeDirsLen; ++i) {
183 0 : size += mSafeDirs[i].SizeOfExcludingThis(aMallocSizeOf);
184 : }
185 0 : return size;
186 : }
187 :
188 : private:
189 : enum Stage
190 : {
191 : STAGE_STARTUP = 0,
192 : STAGE_NORMAL,
193 : STAGE_SHUTDOWN,
194 : NUM_STAGES
195 : };
196 0 : static inline Stage NextStage(Stage aStage)
197 : {
198 0 : switch (aStage) {
199 : case STAGE_STARTUP:
200 0 : return STAGE_NORMAL;
201 : case STAGE_NORMAL:
202 0 : return STAGE_SHUTDOWN;
203 : case STAGE_SHUTDOWN:
204 0 : return STAGE_SHUTDOWN;
205 : default:
206 0 : return NUM_STAGES;
207 : }
208 : }
209 :
210 0 : struct FileStatsByStage
211 : {
212 : FileStats mStats[NUM_STAGES];
213 : };
214 : typedef nsBaseHashtableET<nsStringHashKey, FileStatsByStage> FileIOEntryType;
215 :
216 : // Statistics for each filename
217 : AutoHashtable<FileIOEntryType> mFileStats;
218 : // Container for whitelisted directories
219 : nsTArray<SafeDir> mSafeDirs;
220 : Stage mCurStage;
221 :
222 : /**
223 : * Reflect a FileIOEntryType object to a Javascript property on obj with
224 : * filename as key containing array:
225 : * [totalTime, creates, reads, writes, fsyncs, stats]
226 : */
227 : static bool ReflectFileStats(FileIOEntryType* entry, JSContext *cx,
228 : JS::Handle<JSObject*> obj);
229 : };
230 :
231 1 : TelemetryIOInterposeObserver::TelemetryIOInterposeObserver(nsIFile* aXreDir)
232 1 : : mCurStage(STAGE_STARTUP)
233 : {
234 2 : nsAutoString xreDirPath;
235 1 : nsresult rv = aXreDir->GetPath(xreDirPath);
236 1 : if (NS_SUCCEEDED(rv)) {
237 1 : AddPath(xreDirPath, NS_LITERAL_STRING("{xre}"));
238 : }
239 1 : }
240 :
241 2 : void TelemetryIOInterposeObserver::AddPath(const nsAString& aPath,
242 : const nsAString& aSubstName)
243 : {
244 2 : mSafeDirs.AppendElement(SafeDir(aPath, aSubstName));
245 2 : }
246 :
247 : // Threshold for reporting slow main-thread I/O (50 milliseconds).
248 3 : const TimeDuration kTelemetryReportThreshold = TimeDuration::FromMilliseconds(50);
249 :
250 1868 : void TelemetryIOInterposeObserver::Observe(Observation& aOb)
251 : {
252 : // We only report main-thread I/O
253 1868 : if (!IsMainThread()) {
254 2494 : return;
255 : }
256 :
257 1242 : if (aOb.ObservedOperation() == OpNextStage) {
258 0 : mCurStage = NextStage(mCurStage);
259 0 : MOZ_ASSERT(mCurStage < NUM_STAGES);
260 0 : return;
261 : }
262 :
263 1242 : if (aOb.Duration() < kTelemetryReportThreshold) {
264 1242 : return;
265 : }
266 :
267 : // Get the filename
268 0 : const char16_t* filename = aOb.Filename();
269 :
270 : // Discard observations without filename
271 0 : if (!filename) {
272 0 : return;
273 : }
274 :
275 : #if defined(XP_WIN)
276 : nsCaseInsensitiveStringComparator comparator;
277 : #else
278 0 : nsDefaultStringComparator comparator;
279 : #endif
280 0 : nsAutoString processedName;
281 0 : nsDependentString filenameStr(filename);
282 0 : uint32_t safeDirsLen = mSafeDirs.Length();
283 0 : for (uint32_t i = 0; i < safeDirsLen; ++i) {
284 0 : if (StringBeginsWith(filenameStr, mSafeDirs[i].mPath, comparator)) {
285 0 : processedName = mSafeDirs[i].mSubstName;
286 0 : processedName += Substring(filenameStr, mSafeDirs[i].mPath.Length());
287 0 : break;
288 : }
289 : }
290 :
291 0 : if (processedName.IsEmpty()) {
292 0 : return;
293 : }
294 :
295 : // Create a new entry or retrieve the existing one
296 0 : FileIOEntryType* entry = mFileStats.PutEntry(processedName);
297 0 : if (entry) {
298 0 : FileStats& stats = entry->mData.mStats[mCurStage];
299 : // Update the statistics
300 0 : stats.totalTime += (double) aOb.Duration().ToMilliseconds();
301 0 : switch (aOb.ObservedOperation()) {
302 : case OpCreateOrOpen:
303 0 : stats.creates++;
304 0 : break;
305 : case OpRead:
306 0 : stats.reads++;
307 0 : break;
308 : case OpWrite:
309 0 : stats.writes++;
310 0 : break;
311 : case OpFSync:
312 0 : stats.fsyncs++;
313 0 : break;
314 : case OpStat:
315 0 : stats.stats++;
316 0 : break;
317 : default:
318 0 : break;
319 : }
320 : }
321 : }
322 :
323 0 : bool TelemetryIOInterposeObserver::ReflectFileStats(FileIOEntryType* entry,
324 : JSContext *cx,
325 : JS::Handle<JSObject*> obj)
326 : {
327 0 : JS::AutoValueArray<NUM_STAGES> stages(cx);
328 :
329 0 : FileStatsByStage& statsByStage = entry->mData;
330 0 : for (int s = STAGE_STARTUP; s < NUM_STAGES; ++s) {
331 0 : FileStats& fileStats = statsByStage.mStats[s];
332 :
333 0 : if (fileStats.totalTime == 0 && fileStats.creates == 0 &&
334 0 : fileStats.reads == 0 && fileStats.writes == 0 &&
335 0 : fileStats.fsyncs == 0 && fileStats.stats == 0) {
336 : // Don't add an array that contains no information
337 0 : stages[s].setNull();
338 0 : continue;
339 : }
340 :
341 : // Array we want to report
342 0 : JS::AutoValueArray<6> stats(cx);
343 0 : stats[0].setNumber(fileStats.totalTime);
344 0 : stats[1].setNumber(fileStats.creates);
345 0 : stats[2].setNumber(fileStats.reads);
346 0 : stats[3].setNumber(fileStats.writes);
347 0 : stats[4].setNumber(fileStats.fsyncs);
348 0 : stats[5].setNumber(fileStats.stats);
349 :
350 : // Create jsStats as array of elements above
351 0 : JS::RootedObject jsStats(cx, JS_NewArrayObject(cx, stats));
352 0 : if (!jsStats) {
353 0 : continue;
354 : }
355 :
356 0 : stages[s].setObject(*jsStats);
357 : }
358 :
359 0 : JS::Rooted<JSObject*> jsEntry(cx, JS_NewArrayObject(cx, stages));
360 0 : if (!jsEntry) {
361 0 : return false;
362 : }
363 :
364 : // Add jsEntry to top-level dictionary
365 0 : const nsAString& key = entry->GetKey();
366 0 : return JS_DefineUCProperty(cx, obj, key.Data(), key.Length(),
367 0 : jsEntry, JSPROP_ENUMERATE | JSPROP_READONLY);
368 : }
369 :
370 0 : bool TelemetryIOInterposeObserver::ReflectIntoJS(JSContext *cx,
371 : JS::Handle<JSObject*> rootObj)
372 : {
373 0 : return mFileStats.ReflectIntoJS(ReflectFileStats, cx, rootObj);
374 : }
375 :
376 : // This is not a member of TelemetryImpl because we want to record I/O during
377 : // startup.
378 3 : StaticAutoPtr<TelemetryIOInterposeObserver> sTelemetryIOObserver;
379 :
380 : void
381 0 : ClearIOReporting()
382 : {
383 0 : if (!sTelemetryIOObserver) {
384 0 : return;
385 : }
386 : IOInterposer::Unregister(IOInterposeObserver::OpAllWithStaging,
387 0 : sTelemetryIOObserver);
388 0 : sTelemetryIOObserver = nullptr;
389 : }
390 :
391 : class TelemetryImpl final
392 : : public nsITelemetry
393 : , public nsIMemoryReporter
394 : {
395 : NS_DECL_THREADSAFE_ISUPPORTS
396 : NS_DECL_NSITELEMETRY
397 : NS_DECL_NSIMEMORYREPORTER
398 :
399 : public:
400 : void InitMemoryReporter();
401 :
402 : static already_AddRefed<nsITelemetry> CreateTelemetryInstance();
403 : static void ShutdownTelemetry();
404 : static void RecordSlowStatement(const nsACString &sql, const nsACString &dbName,
405 : uint32_t delay);
406 : #if defined(MOZ_GECKO_PROFILER)
407 : static void RecordChromeHang(uint32_t aDuration,
408 : Telemetry::ProcessedStack &aStack,
409 : int32_t aSystemUptime,
410 : int32_t aFirefoxUptime,
411 : HangAnnotationsPtr aAnnotations);
412 : #endif
413 : #if defined(MOZ_GECKO_PROFILER)
414 : static void DoStackCapture(const nsACString& aKey);
415 : #endif
416 : static void RecordThreadHangStats(Telemetry::ThreadHangStats&& aStats);
417 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
418 : struct Stat {
419 : uint32_t hitCount;
420 : uint32_t totalTime;
421 : };
422 : struct StmtStats {
423 : struct Stat mainThread;
424 : struct Stat otherThreads;
425 : };
426 : typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType;
427 :
428 : static void RecordIceCandidates(const uint32_t iceCandidateBitmask,
429 : const bool success);
430 : static bool CanRecordBase();
431 : static bool CanRecordExtended();
432 : private:
433 : TelemetryImpl();
434 : ~TelemetryImpl();
435 :
436 : static nsCString SanitizeSQL(const nsACString& sql);
437 :
438 : enum SanitizedState { Sanitized, Unsanitized };
439 :
440 : static void StoreSlowSQL(const nsACString &offender, uint32_t delay,
441 : SanitizedState state);
442 :
443 : static bool ReflectMainThreadSQL(SlowSQLEntryType *entry, JSContext *cx,
444 : JS::Handle<JSObject*> obj);
445 : static bool ReflectOtherThreadsSQL(SlowSQLEntryType *entry, JSContext *cx,
446 : JS::Handle<JSObject*> obj);
447 : static bool ReflectSQL(const SlowSQLEntryType *entry, const Stat *stat,
448 : JSContext *cx, JS::Handle<JSObject*> obj);
449 :
450 : bool AddSQLInfo(JSContext *cx, JS::Handle<JSObject*> rootObj, bool mainThread,
451 : bool privateSQL);
452 : bool GetSQLStats(JSContext *cx, JS::MutableHandle<JS::Value> ret,
453 : bool includePrivateSql);
454 :
455 : void ReadLateWritesStacks(nsIFile* aProfileDir);
456 :
457 : static TelemetryImpl *sTelemetry;
458 : AutoHashtable<SlowSQLEntryType> mPrivateSQL;
459 : AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
460 : Mutex mHashMutex;
461 : HangReports mHangReports;
462 : Mutex mHangReportsMutex;
463 : Atomic<bool> mCanRecordBase;
464 : Atomic<bool> mCanRecordExtended;
465 :
466 : #if defined(MOZ_GECKO_PROFILER)
467 : // Stores data about stacks captured on demand.
468 : KeyedStackCapturer mStackCapturer;
469 : #endif
470 :
471 : // mThreadHangStats stores recorded, inactive thread hang stats
472 : Vector<Telemetry::ThreadHangStats> mThreadHangStats;
473 : Mutex mThreadHangStatsMutex;
474 :
475 : CombinedStacks mLateWritesStacks; // This is collected out of the main thread.
476 : bool mCachedTelemetryData;
477 : uint32_t mLastShutdownTime;
478 : uint32_t mFailedLockCount;
479 : nsCOMArray<nsIFetchTelemetryDataCallback> mCallbacks;
480 : friend class nsFetchTelemetryData;
481 :
482 : WebrtcTelemetry mWebrtcTelemetry;
483 : };
484 :
485 : TelemetryImpl* TelemetryImpl::sTelemetry = nullptr;
486 :
487 0 : MOZ_DEFINE_MALLOC_SIZE_OF(TelemetryMallocSizeOf)
488 :
489 : NS_IMETHODIMP
490 0 : TelemetryImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
491 : nsISupports* aData, bool aAnonymize)
492 : {
493 0 : MOZ_COLLECT_REPORT(
494 : "explicit/telemetry", KIND_HEAP, UNITS_BYTES,
495 : SizeOfIncludingThis(TelemetryMallocSizeOf),
496 0 : "Memory used by the telemetry system.");
497 :
498 0 : return NS_OK;
499 : }
500 :
501 : void
502 3 : InitHistogramRecordingEnabled()
503 : {
504 3 : TelemetryHistogram::InitHistogramRecordingEnabled();
505 3 : }
506 :
507 : static uint32_t
508 0 : ReadLastShutdownDuration(const char *filename) {
509 0 : FILE *f = fopen(filename, "r");
510 0 : if (!f) {
511 0 : return 0;
512 : }
513 :
514 : int shutdownTime;
515 0 : int r = fscanf(f, "%d\n", &shutdownTime);
516 0 : fclose(f);
517 0 : if (r != 1) {
518 0 : return 0;
519 : }
520 :
521 0 : return shutdownTime;
522 : }
523 :
524 : const int32_t kMaxFailedProfileLockFileSize = 10;
525 :
526 : bool
527 0 : GetFailedLockCount(nsIInputStream* inStream, uint32_t aCount,
528 : unsigned int& result)
529 : {
530 0 : nsAutoCString bufStr;
531 : nsresult rv;
532 0 : rv = NS_ReadInputStreamToString(inStream, bufStr, aCount);
533 0 : NS_ENSURE_SUCCESS(rv, false);
534 0 : result = bufStr.ToInteger(&rv);
535 0 : return NS_SUCCEEDED(rv) && result > 0;
536 : }
537 :
538 : nsresult
539 0 : GetFailedProfileLockFile(nsIFile* *aFile, nsIFile* aProfileDir)
540 : {
541 0 : NS_ENSURE_ARG_POINTER(aProfileDir);
542 :
543 0 : nsresult rv = aProfileDir->Clone(aFile);
544 0 : NS_ENSURE_SUCCESS(rv, rv);
545 :
546 0 : (*aFile)->AppendNative(NS_LITERAL_CSTRING("Telemetry.FailedProfileLocks.txt"));
547 0 : return NS_OK;
548 : }
549 :
550 0 : class nsFetchTelemetryData : public Runnable
551 : {
552 : public:
553 0 : nsFetchTelemetryData(const char* aShutdownTimeFilename,
554 : nsIFile* aFailedProfileLockFile,
555 : nsIFile* aProfileDir)
556 0 : : mozilla::Runnable("nsFetchTelemetryData")
557 : , mShutdownTimeFilename(aShutdownTimeFilename)
558 : , mFailedProfileLockFile(aFailedProfileLockFile)
559 : , mTelemetry(TelemetryImpl::sTelemetry)
560 0 : , mProfileDir(aProfileDir)
561 : {
562 0 : }
563 :
564 : private:
565 : const char* mShutdownTimeFilename;
566 : nsCOMPtr<nsIFile> mFailedProfileLockFile;
567 : RefPtr<TelemetryImpl> mTelemetry;
568 : nsCOMPtr<nsIFile> mProfileDir;
569 :
570 : public:
571 0 : void MainThread() {
572 0 : mTelemetry->mCachedTelemetryData = true;
573 0 : for (unsigned int i = 0, n = mTelemetry->mCallbacks.Count(); i < n; ++i) {
574 0 : mTelemetry->mCallbacks[i]->Complete();
575 : }
576 0 : mTelemetry->mCallbacks.Clear();
577 0 : }
578 :
579 0 : NS_IMETHOD Run() override {
580 0 : LoadFailedLockCount(mTelemetry->mFailedLockCount);
581 0 : mTelemetry->mLastShutdownTime =
582 0 : ReadLastShutdownDuration(mShutdownTimeFilename);
583 0 : mTelemetry->ReadLateWritesStacks(mProfileDir);
584 : nsCOMPtr<nsIRunnable> e =
585 0 : NewRunnableMethod("nsFetchTelemetryData::MainThread",
586 : this,
587 0 : &nsFetchTelemetryData::MainThread);
588 0 : NS_ENSURE_STATE(e);
589 0 : NS_DispatchToMainThread(e);
590 0 : return NS_OK;
591 : }
592 :
593 : private:
594 : nsresult
595 0 : LoadFailedLockCount(uint32_t& failedLockCount)
596 : {
597 0 : failedLockCount = 0;
598 0 : int64_t fileSize = 0;
599 0 : nsresult rv = mFailedProfileLockFile->GetFileSize(&fileSize);
600 0 : if (NS_FAILED(rv)) {
601 0 : return rv;
602 : }
603 0 : NS_ENSURE_TRUE(fileSize <= kMaxFailedProfileLockFileSize,
604 : NS_ERROR_UNEXPECTED);
605 0 : nsCOMPtr<nsIInputStream> inStream;
606 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream),
607 : mFailedProfileLockFile,
608 0 : PR_RDONLY);
609 0 : NS_ENSURE_SUCCESS(rv, rv);
610 0 : NS_ENSURE_TRUE(GetFailedLockCount(inStream, fileSize, failedLockCount),
611 : NS_ERROR_UNEXPECTED);
612 0 : inStream->Close();
613 :
614 0 : mFailedProfileLockFile->Remove(false);
615 0 : return NS_OK;
616 : }
617 : };
618 :
619 : static TimeStamp gRecordedShutdownStartTime;
620 : static bool gAlreadyFreedShutdownTimeFileName = false;
621 : static char *gRecordedShutdownTimeFileName = nullptr;
622 :
623 : static char *
624 0 : GetShutdownTimeFileName()
625 : {
626 0 : if (gAlreadyFreedShutdownTimeFileName) {
627 0 : return nullptr;
628 : }
629 :
630 0 : if (!gRecordedShutdownTimeFileName) {
631 0 : nsCOMPtr<nsIFile> mozFile;
632 0 : NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
633 0 : if (!mozFile)
634 0 : return nullptr;
635 :
636 0 : mozFile->AppendNative(NS_LITERAL_CSTRING("Telemetry.ShutdownTime.txt"));
637 0 : nsAutoCString nativePath;
638 0 : nsresult rv = mozFile->GetNativePath(nativePath);
639 0 : if (!NS_SUCCEEDED(rv))
640 0 : return nullptr;
641 :
642 0 : gRecordedShutdownTimeFileName = PL_strdup(nativePath.get());
643 : }
644 :
645 0 : return gRecordedShutdownTimeFileName;
646 : }
647 :
648 : NS_IMETHODIMP
649 0 : TelemetryImpl::GetLastShutdownDuration(uint32_t *aResult)
650 : {
651 : // The user must call AsyncFetchTelemetryData first. We return zero instead of
652 : // reporting a failure so that the rest of telemetry can uniformly handle
653 : // the read not being available yet.
654 0 : if (!mCachedTelemetryData) {
655 0 : *aResult = 0;
656 0 : return NS_OK;
657 : }
658 :
659 0 : *aResult = mLastShutdownTime;
660 0 : return NS_OK;
661 : }
662 :
663 : NS_IMETHODIMP
664 0 : TelemetryImpl::GetFailedProfileLockCount(uint32_t* aResult)
665 : {
666 : // The user must call AsyncFetchTelemetryData first. We return zero instead of
667 : // reporting a failure so that the rest of telemetry can uniformly handle
668 : // the read not being available yet.
669 0 : if (!mCachedTelemetryData) {
670 0 : *aResult = 0;
671 0 : return NS_OK;
672 : }
673 :
674 0 : *aResult = mFailedLockCount;
675 0 : return NS_OK;
676 : }
677 :
678 : NS_IMETHODIMP
679 0 : TelemetryImpl::AsyncFetchTelemetryData(nsIFetchTelemetryDataCallback *aCallback)
680 : {
681 : // We have finished reading the data already, just call the callback.
682 0 : if (mCachedTelemetryData) {
683 0 : aCallback->Complete();
684 0 : return NS_OK;
685 : }
686 :
687 : // We already have a read request running, just remember the callback.
688 0 : if (mCallbacks.Count() != 0) {
689 0 : mCallbacks.AppendObject(aCallback);
690 0 : return NS_OK;
691 : }
692 :
693 : // We make this check so that GetShutdownTimeFileName() doesn't get
694 : // called; calling that function without telemetry enabled violates
695 : // assumptions that the write-the-shutdown-timestamp machinery makes.
696 0 : if (!Telemetry::CanRecordExtended()) {
697 0 : mCachedTelemetryData = true;
698 0 : aCallback->Complete();
699 0 : return NS_OK;
700 : }
701 :
702 : // Send the read to a background thread provided by the stream transport
703 : // service to avoid a read in the main thread.
704 : nsCOMPtr<nsIEventTarget> targetThread =
705 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
706 0 : if (!targetThread) {
707 0 : mCachedTelemetryData = true;
708 0 : aCallback->Complete();
709 0 : return NS_OK;
710 : }
711 :
712 : // We have to get the filename from the main thread.
713 0 : const char *shutdownTimeFilename = GetShutdownTimeFileName();
714 0 : if (!shutdownTimeFilename) {
715 0 : mCachedTelemetryData = true;
716 0 : aCallback->Complete();
717 0 : return NS_OK;
718 : }
719 :
720 0 : nsCOMPtr<nsIFile> profileDir;
721 0 : nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
722 0 : getter_AddRefs(profileDir));
723 0 : if (NS_FAILED(rv)) {
724 0 : mCachedTelemetryData = true;
725 0 : aCallback->Complete();
726 0 : return NS_OK;
727 : }
728 :
729 0 : nsCOMPtr<nsIFile> failedProfileLockFile;
730 0 : rv = GetFailedProfileLockFile(getter_AddRefs(failedProfileLockFile),
731 0 : profileDir);
732 0 : if (NS_FAILED(rv)) {
733 0 : mCachedTelemetryData = true;
734 0 : aCallback->Complete();
735 0 : return NS_OK;
736 : }
737 :
738 0 : mCallbacks.AppendObject(aCallback);
739 :
740 : nsCOMPtr<nsIRunnable> event = new nsFetchTelemetryData(shutdownTimeFilename,
741 : failedProfileLockFile,
742 0 : profileDir);
743 :
744 0 : targetThread->Dispatch(event, NS_DISPATCH_NORMAL);
745 0 : return NS_OK;
746 : }
747 :
748 3 : TelemetryImpl::TelemetryImpl()
749 : : mHashMutex("Telemetry::mHashMutex")
750 : , mHangReportsMutex("Telemetry::mHangReportsMutex")
751 : , mCanRecordBase(false)
752 : , mCanRecordExtended(false)
753 : , mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex")
754 : , mCachedTelemetryData(false)
755 : , mLastShutdownTime(0)
756 3 : , mFailedLockCount(0)
757 : {
758 : // We expect TelemetryHistogram::InitializeGlobalState() to have been
759 : // called before we get to this point.
760 3 : MOZ_ASSERT(TelemetryHistogram::GlobalStateHasBeenInitialized());
761 3 : }
762 :
763 0 : TelemetryImpl::~TelemetryImpl() {
764 0 : UnregisterWeakMemoryReporter(this);
765 :
766 : // This is still racey as access to these collections is guarded using sTelemetry.
767 : // We will fix this in bug 1367344.
768 0 : MutexAutoLock hashLock(mHashMutex);
769 0 : MutexAutoLock hangReportsLock(mHangReportsMutex);
770 0 : MutexAutoLock threadHangsLock(mThreadHangStatsMutex);
771 0 : }
772 :
773 : void
774 3 : TelemetryImpl::InitMemoryReporter() {
775 3 : RegisterWeakMemoryReporter(this);
776 3 : }
777 :
778 : bool
779 0 : TelemetryImpl::ReflectSQL(const SlowSQLEntryType *entry,
780 : const Stat *stat,
781 : JSContext *cx,
782 : JS::Handle<JSObject*> obj)
783 : {
784 0 : if (stat->hitCount == 0)
785 0 : return true;
786 :
787 0 : const nsACString &sql = entry->GetKey();
788 :
789 0 : JS::Rooted<JSObject*> arrayObj(cx, JS_NewArrayObject(cx, 0));
790 0 : if (!arrayObj) {
791 0 : return false;
792 : }
793 0 : return (JS_DefineElement(cx, arrayObj, 0, stat->hitCount, JSPROP_ENUMERATE)
794 0 : && JS_DefineElement(cx, arrayObj, 1, stat->totalTime, JSPROP_ENUMERATE)
795 0 : && JS_DefineProperty(cx, obj, sql.BeginReading(), arrayObj,
796 0 : JSPROP_ENUMERATE));
797 : }
798 :
799 : bool
800 0 : TelemetryImpl::ReflectMainThreadSQL(SlowSQLEntryType *entry, JSContext *cx,
801 : JS::Handle<JSObject*> obj)
802 : {
803 0 : return ReflectSQL(entry, &entry->mData.mainThread, cx, obj);
804 : }
805 :
806 : bool
807 0 : TelemetryImpl::ReflectOtherThreadsSQL(SlowSQLEntryType *entry, JSContext *cx,
808 : JS::Handle<JSObject*> obj)
809 : {
810 0 : return ReflectSQL(entry, &entry->mData.otherThreads, cx, obj);
811 : }
812 :
813 : bool
814 0 : TelemetryImpl::AddSQLInfo(JSContext *cx, JS::Handle<JSObject*> rootObj, bool mainThread,
815 : bool privateSQL)
816 : {
817 0 : JS::Rooted<JSObject*> statsObj(cx, JS_NewPlainObject(cx));
818 0 : if (!statsObj)
819 0 : return false;
820 :
821 0 : AutoHashtable<SlowSQLEntryType>& sqlMap = (privateSQL ? mPrivateSQL : mSanitizedSQL);
822 : AutoHashtable<SlowSQLEntryType>::ReflectEntryFunc reflectFunction =
823 0 : (mainThread ? ReflectMainThreadSQL : ReflectOtherThreadsSQL);
824 0 : if (!sqlMap.ReflectIntoJS(reflectFunction, cx, statsObj)) {
825 0 : return false;
826 : }
827 :
828 0 : return JS_DefineProperty(cx, rootObj,
829 : mainThread ? "mainThread" : "otherThreads",
830 0 : statsObj, JSPROP_ENUMERATE);
831 : }
832 :
833 : NS_IMETHODIMP
834 0 : TelemetryImpl::SetHistogramRecordingEnabled(const nsACString &id, bool aEnabled)
835 : {
836 0 : return TelemetryHistogram::SetHistogramRecordingEnabled(id, aEnabled);
837 : }
838 :
839 : NS_IMETHODIMP
840 0 : TelemetryImpl::GetHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
841 : {
842 0 : return TelemetryHistogram::CreateHistogramSnapshots(cx, ret, false, false);
843 : }
844 :
845 : NS_IMETHODIMP
846 0 : TelemetryImpl::SnapshotSubsessionHistograms(bool clearSubsession,
847 : JSContext *cx,
848 : JS::MutableHandle<JS::Value> ret)
849 : {
850 : #if !defined(MOZ_WIDGET_ANDROID)
851 0 : return TelemetryHistogram::CreateHistogramSnapshots(cx, ret, true,
852 0 : clearSubsession);
853 : #else
854 : return NS_OK;
855 : #endif
856 : }
857 :
858 : NS_IMETHODIMP
859 0 : TelemetryImpl::GetKeyedHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
860 : {
861 0 : return TelemetryHistogram::GetKeyedHistogramSnapshots(cx, ret);
862 : }
863 :
864 : bool
865 0 : TelemetryImpl::GetSQLStats(JSContext *cx, JS::MutableHandle<JS::Value> ret, bool includePrivateSql)
866 : {
867 0 : JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
868 0 : if (!root_obj)
869 0 : return false;
870 0 : ret.setObject(*root_obj);
871 :
872 0 : MutexAutoLock hashMutex(mHashMutex);
873 : // Add info about slow SQL queries on the main thread
874 0 : if (!AddSQLInfo(cx, root_obj, true, includePrivateSql))
875 0 : return false;
876 : // Add info about slow SQL queries on other threads
877 0 : if (!AddSQLInfo(cx, root_obj, false, includePrivateSql))
878 0 : return false;
879 :
880 0 : return true;
881 : }
882 :
883 : NS_IMETHODIMP
884 0 : TelemetryImpl::GetSlowSQL(JSContext *cx, JS::MutableHandle<JS::Value> ret)
885 : {
886 0 : if (GetSQLStats(cx, ret, false))
887 0 : return NS_OK;
888 0 : return NS_ERROR_FAILURE;
889 : }
890 :
891 : NS_IMETHODIMP
892 0 : TelemetryImpl::GetDebugSlowSQL(JSContext *cx, JS::MutableHandle<JS::Value> ret)
893 : {
894 : bool revealPrivateSql =
895 0 : Preferences::GetBool("toolkit.telemetry.debugSlowSql", false);
896 0 : if (GetSQLStats(cx, ret, revealPrivateSql))
897 0 : return NS_OK;
898 0 : return NS_ERROR_FAILURE;
899 : }
900 :
901 : NS_IMETHODIMP
902 0 : TelemetryImpl::GetWebrtcStats(JSContext *cx, JS::MutableHandle<JS::Value> ret)
903 : {
904 0 : if (mWebrtcTelemetry.GetWebrtcStats(cx, ret))
905 0 : return NS_OK;
906 0 : return NS_ERROR_FAILURE;
907 : }
908 :
909 : NS_IMETHODIMP
910 0 : TelemetryImpl::GetMaximalNumberOfConcurrentThreads(uint32_t *ret)
911 : {
912 0 : *ret = nsThreadManager::get().GetHighestNumberOfThreads();
913 0 : return NS_OK;
914 : }
915 :
916 : NS_IMETHODIMP
917 0 : TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
918 : {
919 0 : MutexAutoLock hangReportMutex(mHangReportsMutex);
920 :
921 0 : const CombinedStacks& stacks = mHangReports.GetStacks();
922 0 : JS::Rooted<JSObject*> fullReportObj(cx, CreateJSStackObject(cx, stacks));
923 0 : if (!fullReportObj) {
924 0 : return NS_ERROR_FAILURE;
925 : }
926 :
927 0 : ret.setObject(*fullReportObj);
928 :
929 0 : JS::Rooted<JSObject*> durationArray(cx, JS_NewArrayObject(cx, 0));
930 0 : JS::Rooted<JSObject*> systemUptimeArray(cx, JS_NewArrayObject(cx, 0));
931 0 : JS::Rooted<JSObject*> firefoxUptimeArray(cx, JS_NewArrayObject(cx, 0));
932 0 : JS::Rooted<JSObject*> annotationsArray(cx, JS_NewArrayObject(cx, 0));
933 0 : if (!durationArray || !systemUptimeArray || !firefoxUptimeArray ||
934 0 : !annotationsArray) {
935 0 : return NS_ERROR_FAILURE;
936 : }
937 :
938 0 : bool ok = JS_DefineProperty(cx, fullReportObj, "durations",
939 0 : durationArray, JSPROP_ENUMERATE);
940 0 : if (!ok) {
941 0 : return NS_ERROR_FAILURE;
942 : }
943 :
944 0 : ok = JS_DefineProperty(cx, fullReportObj, "systemUptime",
945 0 : systemUptimeArray, JSPROP_ENUMERATE);
946 0 : if (!ok) {
947 0 : return NS_ERROR_FAILURE;
948 : }
949 :
950 0 : ok = JS_DefineProperty(cx, fullReportObj, "firefoxUptime",
951 0 : firefoxUptimeArray, JSPROP_ENUMERATE);
952 0 : if (!ok) {
953 0 : return NS_ERROR_FAILURE;
954 : }
955 :
956 0 : ok = JS_DefineProperty(cx, fullReportObj, "annotations", annotationsArray,
957 0 : JSPROP_ENUMERATE);
958 0 : if (!ok) {
959 0 : return NS_ERROR_FAILURE;
960 : }
961 :
962 :
963 0 : const size_t length = stacks.GetStackCount();
964 0 : for (size_t i = 0; i < length; ++i) {
965 0 : if (!JS_DefineElement(cx, durationArray, i, mHangReports.GetDuration(i),
966 : JSPROP_ENUMERATE)) {
967 0 : return NS_ERROR_FAILURE;
968 : }
969 0 : if (!JS_DefineElement(cx, systemUptimeArray, i, mHangReports.GetSystemUptime(i),
970 : JSPROP_ENUMERATE)) {
971 0 : return NS_ERROR_FAILURE;
972 : }
973 0 : if (!JS_DefineElement(cx, firefoxUptimeArray, i, mHangReports.GetFirefoxUptime(i),
974 : JSPROP_ENUMERATE)) {
975 0 : return NS_ERROR_FAILURE;
976 : }
977 :
978 0 : size_t annotationIndex = 0;
979 : const nsClassHashtable<nsStringHashKey, HangReports::AnnotationInfo>& annotationInfo =
980 0 : mHangReports.GetAnnotationInfo();
981 :
982 0 : for (auto iter = annotationInfo.ConstIter(); !iter.Done(); iter.Next()) {
983 0 : const HangReports::AnnotationInfo* info = iter.Data();
984 :
985 0 : JS::Rooted<JSObject*> keyValueArray(cx, JS_NewArrayObject(cx, 0));
986 0 : if (!keyValueArray) {
987 0 : return NS_ERROR_FAILURE;
988 : }
989 :
990 : // Create an array containing all the indices of the chrome hangs relative to this
991 : // annotation.
992 0 : JS::Rooted<JS::Value> indicesArray(cx);
993 0 : if (!mozilla::dom::ToJSValue(cx, info->mHangIndices, &indicesArray)) {
994 0 : return NS_ERROR_OUT_OF_MEMORY;
995 : }
996 :
997 : // We're saving the annotation as [[indices], {annotation-data}], so add the indices
998 : // array as the first element of that structure.
999 0 : if (!JS_DefineElement(cx, keyValueArray, 0, indicesArray, JSPROP_ENUMERATE)) {
1000 0 : return NS_ERROR_FAILURE;
1001 : }
1002 :
1003 : // Create the annotations object...
1004 0 : JS::Rooted<JSObject*> jsAnnotation(cx, JS_NewPlainObject(cx));
1005 0 : if (!jsAnnotation) {
1006 0 : return NS_ERROR_FAILURE;
1007 : }
1008 : UniquePtr<HangAnnotations::Enumerator> annotationsEnum =
1009 0 : info->mAnnotations->GetEnumerator();
1010 0 : if (!annotationsEnum) {
1011 0 : return NS_ERROR_FAILURE;
1012 : }
1013 :
1014 : // ... fill it with key:value pairs...
1015 0 : nsAutoString key;
1016 0 : nsAutoString value;
1017 0 : while (annotationsEnum->Next(key, value)) {
1018 0 : JS::RootedValue jsValue(cx);
1019 0 : jsValue.setString(JS_NewUCStringCopyN(cx, value.get(), value.Length()));
1020 0 : if (!JS_DefineUCProperty(cx, jsAnnotation, key.get(), key.Length(),
1021 : jsValue, JSPROP_ENUMERATE)) {
1022 0 : return NS_ERROR_FAILURE;
1023 : }
1024 : }
1025 :
1026 : // ... and append it after the indices array.
1027 0 : if (!JS_DefineElement(cx, keyValueArray, 1, jsAnnotation, JSPROP_ENUMERATE)) {
1028 0 : return NS_ERROR_FAILURE;
1029 : }
1030 0 : if (!JS_DefineElement(cx, annotationsArray, annotationIndex++,
1031 : keyValueArray, JSPROP_ENUMERATE)) {
1032 0 : return NS_ERROR_FAILURE;
1033 : }
1034 : }
1035 : }
1036 :
1037 0 : return NS_OK;
1038 : }
1039 :
1040 : NS_IMETHODIMP
1041 0 : TelemetryImpl::SnapshotCapturedStacks(bool clear, JSContext *cx, JS::MutableHandle<JS::Value> ret)
1042 : {
1043 : #if defined(MOZ_GECKO_PROFILER)
1044 0 : nsresult rv = mStackCapturer.ReflectCapturedStacks(cx, ret);
1045 0 : if (clear) {
1046 0 : mStackCapturer.Clear();
1047 : }
1048 0 : return rv;
1049 : #else
1050 : return NS_OK;
1051 : #endif
1052 : }
1053 :
1054 : #if defined(MOZ_GECKO_PROFILER)
1055 0 : class GetLoadedModulesResultRunnable final : public Runnable
1056 : {
1057 : nsMainThreadPtrHandle<Promise> mPromise;
1058 : SharedLibraryInfo mRawModules;
1059 : nsCOMPtr<nsIThread> mWorkerThread;
1060 :
1061 : public:
1062 0 : GetLoadedModulesResultRunnable(const nsMainThreadPtrHandle<Promise>& aPromise,
1063 : const SharedLibraryInfo& rawModules)
1064 0 : : mozilla::Runnable("GetLoadedModulesResultRunnable")
1065 : , mPromise(aPromise)
1066 : , mRawModules(rawModules)
1067 0 : , mWorkerThread(do_GetCurrentThread())
1068 : {
1069 0 : MOZ_ASSERT(!NS_IsMainThread());
1070 0 : }
1071 :
1072 : NS_IMETHOD
1073 0 : Run() override
1074 : {
1075 0 : MOZ_ASSERT(NS_IsMainThread());
1076 :
1077 0 : mWorkerThread->Shutdown();
1078 :
1079 0 : AutoJSAPI jsapi;
1080 0 : if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
1081 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1082 0 : return NS_OK;
1083 : }
1084 :
1085 0 : JSContext* cx = jsapi.cx();
1086 :
1087 0 : JS::RootedObject moduleArray(cx, JS_NewArrayObject(cx, 0));
1088 0 : if (!moduleArray) {
1089 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1090 0 : return NS_OK;
1091 : }
1092 :
1093 0 : for (unsigned int i = 0, n = mRawModules.GetSize(); i != n; i++) {
1094 0 : const SharedLibrary &info = mRawModules.GetEntry(i);
1095 :
1096 0 : JS::RootedObject moduleObj(cx, JS_NewPlainObject(cx));
1097 0 : if (!moduleObj) {
1098 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1099 0 : return NS_OK;
1100 : }
1101 :
1102 : // Module name.
1103 0 : JS::RootedString moduleName(cx, JS_NewUCStringCopyZ(cx, info.GetModuleName().get()));
1104 0 : if (!moduleName || !JS_DefineProperty(cx, moduleObj, "name", moduleName, JSPROP_ENUMERATE)) {
1105 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1106 0 : return NS_OK;
1107 : }
1108 :
1109 : // Module debug name.
1110 0 : JS::RootedValue moduleDebugName(cx);
1111 :
1112 0 : if (!info.GetDebugName().IsEmpty()) {
1113 0 : JS::RootedString str_moduleDebugName(cx, JS_NewUCStringCopyZ(cx, info.GetDebugName().get()));
1114 0 : if (!str_moduleDebugName) {
1115 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1116 0 : return NS_OK;
1117 : }
1118 0 : moduleDebugName.setString(str_moduleDebugName);
1119 : }
1120 : else {
1121 0 : moduleDebugName.setNull();
1122 : }
1123 :
1124 0 : if (!JS_DefineProperty(cx, moduleObj, "debugName", moduleDebugName, JSPROP_ENUMERATE)) {
1125 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1126 0 : return NS_OK;
1127 : }
1128 :
1129 : // Module Breakpad identifier.
1130 0 : JS::RootedValue id(cx);
1131 :
1132 0 : if (!info.GetBreakpadId().empty()) {
1133 0 : JS::RootedString str_id(cx, JS_NewStringCopyZ(cx, info.GetBreakpadId().c_str()));
1134 0 : if (!str_id) {
1135 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1136 0 : return NS_OK;
1137 : }
1138 0 : id.setString(str_id);
1139 : } else {
1140 0 : id.setNull();
1141 : }
1142 :
1143 0 : if (!JS_DefineProperty(cx, moduleObj, "debugID", id, JSPROP_ENUMERATE)) {
1144 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1145 0 : return NS_OK;
1146 : }
1147 :
1148 : // Module version.
1149 0 : JS::RootedValue version(cx);
1150 :
1151 0 : if (!info.GetVersion().empty()) {
1152 0 : JS::RootedString v(cx, JS_NewStringCopyZ(cx, info.GetVersion().c_str()));
1153 0 : if (!v) {
1154 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1155 0 : return NS_OK;
1156 : }
1157 0 : version.setString(v);
1158 : } else {
1159 0 : version.setNull();
1160 : }
1161 :
1162 0 : if (!JS_DefineProperty(cx, moduleObj, "version", version, JSPROP_ENUMERATE)) {
1163 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1164 0 : return NS_OK;
1165 : }
1166 :
1167 0 : if (!JS_DefineElement(cx, moduleArray, i, moduleObj, JSPROP_ENUMERATE)) {
1168 0 : mPromise->MaybeReject(NS_ERROR_FAILURE);
1169 0 : return NS_OK;
1170 : }
1171 : }
1172 :
1173 0 : mPromise->MaybeResolve(moduleArray);
1174 0 : return NS_OK;
1175 : }
1176 : };
1177 :
1178 0 : class GetLoadedModulesRunnable final : public Runnable
1179 : {
1180 : nsMainThreadPtrHandle<Promise> mPromise;
1181 :
1182 : public:
1183 0 : explicit GetLoadedModulesRunnable(
1184 : const nsMainThreadPtrHandle<Promise>& aPromise)
1185 0 : : mozilla::Runnable("GetLoadedModulesRunnable")
1186 0 : , mPromise(aPromise)
1187 0 : { }
1188 :
1189 : NS_IMETHOD
1190 0 : Run() override
1191 : {
1192 0 : nsCOMPtr<nsIRunnable> resultRunnable = new GetLoadedModulesResultRunnable(mPromise, SharedLibraryInfo::GetInfoForSelf());
1193 0 : return NS_DispatchToMainThread(resultRunnable);
1194 : }
1195 : };
1196 : #endif // MOZ_GECKO_PROFILER
1197 :
1198 : NS_IMETHODIMP
1199 0 : TelemetryImpl::GetLoadedModules(JSContext *cx, nsISupports** aPromise)
1200 : {
1201 : #if defined(MOZ_GECKO_PROFILER)
1202 0 : nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
1203 0 : if (NS_WARN_IF(!global)) {
1204 0 : return NS_ERROR_FAILURE;
1205 : }
1206 :
1207 0 : ErrorResult result;
1208 0 : RefPtr<Promise> promise = Promise::Create(global, result);
1209 0 : if (NS_WARN_IF(result.Failed())) {
1210 0 : return result.StealNSResult();
1211 : }
1212 :
1213 0 : nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
1214 0 : nsCOMPtr<nsIThread> getModulesThread;
1215 0 : nsresult rv = tm->NewThread(0, 0, getter_AddRefs(getModulesThread));
1216 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1217 0 : promise->MaybeReject(NS_ERROR_FAILURE);
1218 0 : return NS_OK;
1219 : }
1220 :
1221 : nsMainThreadPtrHandle<Promise> mainThreadPromise(
1222 0 : new nsMainThreadPtrHolder<Promise>("Promise", promise));
1223 0 : nsCOMPtr<nsIRunnable> runnable = new GetLoadedModulesRunnable(mainThreadPromise);
1224 0 : promise.forget(aPromise);
1225 :
1226 0 : return getModulesThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
1227 : #else // MOZ_GECKO_PROFILER
1228 : return NS_ERROR_NOT_IMPLEMENTED;
1229 : #endif // MOZ_GECKO_PROFILER
1230 : }
1231 :
1232 : static bool
1233 0 : IsValidBreakpadId(const std::string &breakpadId)
1234 : {
1235 0 : if (breakpadId.size() < 33) {
1236 0 : return false;
1237 : }
1238 0 : for (char c : breakpadId) {
1239 0 : if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
1240 0 : return false;
1241 : }
1242 : }
1243 0 : return true;
1244 : }
1245 :
1246 : // Read a stack from the given file name. In case of any error, aStack is
1247 : // unchanged.
1248 : static void
1249 0 : ReadStack(const char *aFileName, Telemetry::ProcessedStack &aStack)
1250 : {
1251 0 : std::ifstream file(aFileName);
1252 :
1253 : size_t numModules;
1254 0 : file >> numModules;
1255 0 : if (file.fail()) {
1256 0 : return;
1257 : }
1258 :
1259 0 : char newline = file.get();
1260 0 : if (file.fail() || newline != '\n') {
1261 0 : return;
1262 : }
1263 :
1264 0 : Telemetry::ProcessedStack stack;
1265 0 : for (size_t i = 0; i < numModules; ++i) {
1266 0 : std::string breakpadId;
1267 0 : file >> breakpadId;
1268 0 : if (file.fail() || !IsValidBreakpadId(breakpadId)) {
1269 0 : return;
1270 : }
1271 :
1272 0 : char space = file.get();
1273 0 : if (file.fail() || space != ' ') {
1274 0 : return;
1275 : }
1276 :
1277 0 : std::string moduleName;
1278 0 : getline(file, moduleName);
1279 0 : if (file.fail() || moduleName[0] == ' ') {
1280 0 : return;
1281 : }
1282 :
1283 : Telemetry::ProcessedStack::Module module = {
1284 0 : NS_ConvertUTF8toUTF16(moduleName.c_str()),
1285 : breakpadId
1286 0 : };
1287 0 : stack.AddModule(module);
1288 : }
1289 :
1290 : size_t numFrames;
1291 0 : file >> numFrames;
1292 0 : if (file.fail()) {
1293 0 : return;
1294 : }
1295 :
1296 0 : newline = file.get();
1297 0 : if (file.fail() || newline != '\n') {
1298 0 : return;
1299 : }
1300 :
1301 0 : for (size_t i = 0; i < numFrames; ++i) {
1302 : uint16_t index;
1303 0 : file >> index;
1304 : uintptr_t offset;
1305 0 : file >> std::hex >> offset >> std::dec;
1306 0 : if (file.fail()) {
1307 0 : return;
1308 : }
1309 :
1310 : Telemetry::ProcessedStack::Frame frame = {
1311 : offset,
1312 : index
1313 0 : };
1314 0 : stack.AddFrame(frame);
1315 : }
1316 :
1317 0 : aStack = stack;
1318 : }
1319 :
1320 : NS_IMETHODIMP
1321 0 : TelemetryImpl::GetThreadHangStats(JSContext* cx, JS::MutableHandle<JS::Value> ret)
1322 : {
1323 0 : JS::RootedObject retObj(cx, JS_NewArrayObject(cx, 0));
1324 0 : if (!retObj) {
1325 0 : return NS_ERROR_FAILURE;
1326 : }
1327 0 : size_t threadIndex = 0;
1328 :
1329 0 : if (!BackgroundHangMonitor::IsDisabled()) {
1330 : /* First add active threads; we need to hold |iter| (and its lock)
1331 : throughout this method to avoid a race condition where a thread can
1332 : be recorded twice if the thread is destroyed while this method is
1333 : running */
1334 0 : BackgroundHangMonitor::ThreadHangStatsIterator iter;
1335 0 : for (Telemetry::ThreadHangStats* histogram = iter.GetNext();
1336 0 : histogram; histogram = iter.GetNext()) {
1337 0 : JS::RootedObject obj(cx, CreateJSThreadHangStats(cx, *histogram));
1338 0 : if (!JS_DefineElement(cx, retObj, threadIndex++, obj, JSPROP_ENUMERATE)) {
1339 0 : return NS_ERROR_FAILURE;
1340 : }
1341 : }
1342 : }
1343 :
1344 : // Add saved threads next
1345 0 : MutexAutoLock autoLock(mThreadHangStatsMutex);
1346 0 : for (auto & stat : mThreadHangStats) {
1347 : JS::RootedObject obj(cx,
1348 0 : CreateJSThreadHangStats(cx, stat));
1349 0 : if (!JS_DefineElement(cx, retObj, threadIndex++, obj, JSPROP_ENUMERATE)) {
1350 0 : return NS_ERROR_FAILURE;
1351 : }
1352 : }
1353 0 : ret.setObject(*retObj);
1354 0 : return NS_OK;
1355 : }
1356 :
1357 : void
1358 0 : TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir)
1359 : {
1360 0 : nsAutoCString nativePath;
1361 0 : nsresult rv = aProfileDir->GetNativePath(nativePath);
1362 0 : if (NS_FAILED(rv)) {
1363 0 : return;
1364 : }
1365 :
1366 0 : const char *name = nativePath.get();
1367 0 : PRDir *dir = PR_OpenDir(name);
1368 0 : if (!dir) {
1369 0 : return;
1370 : }
1371 :
1372 : PRDirEntry *ent;
1373 0 : const char *prefix = "Telemetry.LateWriteFinal-";
1374 0 : unsigned int prefixLen = strlen(prefix);
1375 0 : while ((ent = PR_ReadDir(dir, PR_SKIP_NONE))) {
1376 0 : if (strncmp(prefix, ent->name, prefixLen) != 0) {
1377 0 : continue;
1378 : }
1379 :
1380 0 : nsAutoCString stackNativePath = nativePath;
1381 0 : stackNativePath += XPCOM_FILE_PATH_SEPARATOR;
1382 0 : stackNativePath += nsDependentCString(ent->name);
1383 :
1384 0 : Telemetry::ProcessedStack stack;
1385 0 : ReadStack(stackNativePath.get(), stack);
1386 0 : if (stack.GetStackSize() != 0) {
1387 0 : mLateWritesStacks.AddStack(stack);
1388 : }
1389 : // Delete the file so that we don't report it again on the next run.
1390 0 : PR_Delete(stackNativePath.get());
1391 : }
1392 0 : PR_CloseDir(dir);
1393 : }
1394 :
1395 : NS_IMETHODIMP
1396 0 : TelemetryImpl::GetLateWrites(JSContext *cx, JS::MutableHandle<JS::Value> ret)
1397 : {
1398 : // The user must call AsyncReadTelemetryData first. We return an empty list
1399 : // instead of reporting a failure so that the rest of telemetry can uniformly
1400 : // handle the read not being available yet.
1401 :
1402 : // FIXME: we allocate the js object again and again in the getter. We should
1403 : // figure out a way to cache it. In order to do that we have to call
1404 : // JS_AddNamedObjectRoot. A natural place to do so is in the TelemetryImpl
1405 : // constructor, but it is not clear how to get a JSContext in there.
1406 : // Another option would be to call it in here when we first call
1407 : // CreateJSStackObject, but we would still need to figure out where to call
1408 : // JS_RemoveObjectRoot. Would it be ok to never call JS_RemoveObjectRoot
1409 : // and just set the pointer to nullptr is the telemetry destructor?
1410 :
1411 : JSObject *report;
1412 0 : if (!mCachedTelemetryData) {
1413 0 : CombinedStacks empty;
1414 0 : report = CreateJSStackObject(cx, empty);
1415 : } else {
1416 0 : report = CreateJSStackObject(cx, mLateWritesStacks);
1417 : }
1418 :
1419 0 : if (report == nullptr) {
1420 0 : return NS_ERROR_FAILURE;
1421 : }
1422 :
1423 0 : ret.setObject(*report);
1424 0 : return NS_OK;
1425 : }
1426 :
1427 : NS_IMETHODIMP
1428 0 : TelemetryImpl::RegisteredHistograms(uint32_t aDataset, uint32_t *aCount,
1429 : char*** aHistograms)
1430 : {
1431 : return
1432 0 : TelemetryHistogram::RegisteredHistograms(aDataset, aCount, aHistograms);
1433 : }
1434 :
1435 : NS_IMETHODIMP
1436 0 : TelemetryImpl::RegisteredKeyedHistograms(uint32_t aDataset, uint32_t *aCount,
1437 : char*** aHistograms)
1438 : {
1439 : return
1440 : TelemetryHistogram::RegisteredKeyedHistograms(aDataset, aCount,
1441 0 : aHistograms);
1442 : }
1443 :
1444 : NS_IMETHODIMP
1445 9 : TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx,
1446 : JS::MutableHandle<JS::Value> ret)
1447 : {
1448 9 : return TelemetryHistogram::GetHistogramById(name, cx, ret);
1449 : }
1450 :
1451 : NS_IMETHODIMP
1452 6 : TelemetryImpl::GetKeyedHistogramById(const nsACString &name, JSContext *cx,
1453 : JS::MutableHandle<JS::Value> ret)
1454 : {
1455 6 : return TelemetryHistogram::GetKeyedHistogramById(name, cx, ret);
1456 : }
1457 :
1458 : /**
1459 : * Indicates if Telemetry can record base data (FHR data). This is true if the
1460 : * FHR data reporting service or self-support are enabled.
1461 : *
1462 : * In the unlikely event that adding a new base probe is needed, please check the data
1463 : * collection wiki at https://wiki.mozilla.org/Firefox/Data_Collection and talk to the
1464 : * Telemetry team.
1465 : */
1466 : NS_IMETHODIMP
1467 149 : TelemetryImpl::GetCanRecordBase(bool *ret) {
1468 149 : *ret = mCanRecordBase;
1469 149 : return NS_OK;
1470 : }
1471 :
1472 : NS_IMETHODIMP
1473 3 : TelemetryImpl::SetCanRecordBase(bool canRecord) {
1474 3 : if (canRecord != mCanRecordBase) {
1475 0 : TelemetryHistogram::SetCanRecordBase(canRecord);
1476 0 : TelemetryScalar::SetCanRecordBase(canRecord);
1477 0 : TelemetryEvent::SetCanRecordBase(canRecord);
1478 0 : mCanRecordBase = canRecord;
1479 : }
1480 3 : return NS_OK;
1481 : }
1482 :
1483 : /**
1484 : * Indicates if Telemetry is allowed to record extended data. Returns false if the user
1485 : * hasn't opted into "extended Telemetry" on the Release channel, when the user has
1486 : * explicitly opted out of Telemetry on Nightly/Aurora/Beta or if manually set to false
1487 : * during tests.
1488 : * If the returned value is false, gathering of extended telemetry statistics is disabled.
1489 : */
1490 : NS_IMETHODIMP
1491 49 : TelemetryImpl::GetCanRecordExtended(bool *ret) {
1492 49 : *ret = mCanRecordExtended;
1493 49 : return NS_OK;
1494 : }
1495 :
1496 : NS_IMETHODIMP
1497 3 : TelemetryImpl::SetCanRecordExtended(bool canRecord) {
1498 3 : if (canRecord != mCanRecordExtended) {
1499 3 : TelemetryHistogram::SetCanRecordExtended(canRecord);
1500 3 : TelemetryScalar::SetCanRecordExtended(canRecord);
1501 3 : TelemetryEvent::SetCanRecordExtended(canRecord);
1502 3 : mCanRecordExtended = canRecord;
1503 : }
1504 3 : return NS_OK;
1505 : }
1506 :
1507 :
1508 : NS_IMETHODIMP
1509 1 : TelemetryImpl::GetIsOfficialTelemetry(bool *ret) {
1510 : #if defined(MOZILLA_OFFICIAL) && defined(MOZ_TELEMETRY_REPORTING) && !defined(DEBUG)
1511 : *ret = true;
1512 : #else
1513 1 : *ret = false;
1514 : #endif
1515 1 : return NS_OK;
1516 : }
1517 :
1518 : already_AddRefed<nsITelemetry>
1519 3 : TelemetryImpl::CreateTelemetryInstance()
1520 : {
1521 3 : MOZ_ASSERT(sTelemetry == nullptr, "CreateTelemetryInstance may only be called once, via GetService()");
1522 :
1523 3 : bool useTelemetry = false;
1524 8 : if (XRE_IsParentProcess() ||
1525 3 : XRE_IsContentProcess() ||
1526 0 : XRE_IsGPUProcess())
1527 : {
1528 3 : useTelemetry = true;
1529 : }
1530 :
1531 : // First, initialize the TelemetryHistogram and TelemetryScalar global states.
1532 3 : TelemetryHistogram::InitializeGlobalState(useTelemetry, useTelemetry);
1533 3 : TelemetryScalar::InitializeGlobalState(useTelemetry, useTelemetry);
1534 :
1535 : // Only record events from the parent process.
1536 3 : TelemetryEvent::InitializeGlobalState(XRE_IsParentProcess(), XRE_IsParentProcess());
1537 :
1538 : // Now, create and initialize the Telemetry global state.
1539 3 : sTelemetry = new TelemetryImpl();
1540 :
1541 : // AddRef for the local reference
1542 3 : NS_ADDREF(sTelemetry);
1543 : // AddRef for the caller
1544 6 : nsCOMPtr<nsITelemetry> ret = sTelemetry;
1545 :
1546 3 : sTelemetry->mCanRecordBase = useTelemetry;
1547 3 : sTelemetry->mCanRecordExtended = useTelemetry;
1548 :
1549 3 : sTelemetry->InitMemoryReporter();
1550 3 : InitHistogramRecordingEnabled(); // requires sTelemetry to exist
1551 :
1552 6 : return ret.forget();
1553 : }
1554 :
1555 : void
1556 0 : TelemetryImpl::ShutdownTelemetry()
1557 : {
1558 : // No point in collecting IO beyond this point
1559 0 : ClearIOReporting();
1560 0 : NS_IF_RELEASE(sTelemetry);
1561 :
1562 : // Lastly, de-initialise the TelemetryHistogram and TelemetryScalar global states,
1563 : // so as to release any heap storage that would otherwise be kept alive by it.
1564 0 : TelemetryHistogram::DeInitializeGlobalState();
1565 0 : TelemetryScalar::DeInitializeGlobalState();
1566 0 : TelemetryEvent::DeInitializeGlobalState();
1567 0 : TelemetryIPCAccumulator::DeInitializeGlobalState();
1568 0 : }
1569 :
1570 : void
1571 0 : TelemetryImpl::StoreSlowSQL(const nsACString &sql, uint32_t delay,
1572 : SanitizedState state)
1573 : {
1574 0 : AutoHashtable<SlowSQLEntryType>* slowSQLMap = nullptr;
1575 0 : if (state == Sanitized)
1576 0 : slowSQLMap = &(sTelemetry->mSanitizedSQL);
1577 : else
1578 0 : slowSQLMap = &(sTelemetry->mPrivateSQL);
1579 :
1580 0 : MutexAutoLock hashMutex(sTelemetry->mHashMutex);
1581 :
1582 0 : SlowSQLEntryType *entry = slowSQLMap->GetEntry(sql);
1583 0 : if (!entry) {
1584 0 : entry = slowSQLMap->PutEntry(sql);
1585 0 : if (MOZ_UNLIKELY(!entry))
1586 0 : return;
1587 0 : entry->mData.mainThread.hitCount = 0;
1588 0 : entry->mData.mainThread.totalTime = 0;
1589 0 : entry->mData.otherThreads.hitCount = 0;
1590 0 : entry->mData.otherThreads.totalTime = 0;
1591 : }
1592 :
1593 0 : if (NS_IsMainThread()) {
1594 0 : entry->mData.mainThread.hitCount++;
1595 0 : entry->mData.mainThread.totalTime += delay;
1596 : } else {
1597 0 : entry->mData.otherThreads.hitCount++;
1598 0 : entry->mData.otherThreads.totalTime += delay;
1599 : }
1600 : }
1601 :
1602 : /**
1603 : * This method replaces string literals in SQL strings with the word :private
1604 : *
1605 : * States used in this state machine:
1606 : *
1607 : * NORMAL:
1608 : * - This is the active state when not iterating over a string literal or
1609 : * comment
1610 : *
1611 : * SINGLE_QUOTE:
1612 : * - Defined here: http://www.sqlite.org/lang_expr.html
1613 : * - This state represents iterating over a string literal opened with
1614 : * a single quote.
1615 : * - A single quote within the string can be encoded by putting 2 single quotes
1616 : * in a row, e.g. 'This literal contains an escaped quote '''
1617 : * - Any double quotes found within a single-quoted literal are ignored
1618 : * - This state covers BLOB literals, e.g. X'ABC123'
1619 : * - The string literal and the enclosing quotes will be replaced with
1620 : * the text :private
1621 : *
1622 : * DOUBLE_QUOTE:
1623 : * - Same rules as the SINGLE_QUOTE state.
1624 : * - According to http://www.sqlite.org/lang_keywords.html,
1625 : * SQLite interprets text in double quotes as an identifier unless it's used in
1626 : * a context where it cannot be resolved to an identifier and a string literal
1627 : * is allowed. This method removes text in double-quotes for safety.
1628 : *
1629 : * DASH_COMMENT:
1630 : * - http://www.sqlite.org/lang_comment.html
1631 : * - A dash comment starts with two dashes in a row,
1632 : * e.g. DROP TABLE foo -- a comment
1633 : * - Any text following two dashes in a row is interpreted as a comment until
1634 : * end of input or a newline character
1635 : * - Any quotes found within the comment are ignored and no replacements made
1636 : *
1637 : * C_STYLE_COMMENT:
1638 : * - http://www.sqlite.org/lang_comment.html
1639 : * - A C-style comment starts with a forward slash and an asterisk, and ends
1640 : * with an asterisk and a forward slash
1641 : * - Any text following comment start is interpreted as a comment up to end of
1642 : * input or comment end
1643 : * - Any quotes found within the comment are ignored and no replacements made
1644 : */
1645 : nsCString
1646 0 : TelemetryImpl::SanitizeSQL(const nsACString &sql) {
1647 0 : nsCString output;
1648 0 : int length = sql.Length();
1649 :
1650 : typedef enum {
1651 : NORMAL,
1652 : SINGLE_QUOTE,
1653 : DOUBLE_QUOTE,
1654 : DASH_COMMENT,
1655 : C_STYLE_COMMENT,
1656 : } State;
1657 :
1658 0 : State state = NORMAL;
1659 0 : int fragmentStart = 0;
1660 0 : for (int i = 0; i < length; i++) {
1661 0 : char character = sql[i];
1662 0 : char nextCharacter = (i + 1 < length) ? sql[i + 1] : '\0';
1663 :
1664 0 : switch (character) {
1665 : case '\'':
1666 : case '"':
1667 0 : if (state == NORMAL) {
1668 0 : state = (character == '\'') ? SINGLE_QUOTE : DOUBLE_QUOTE;
1669 0 : output += nsDependentCSubstring(sql, fragmentStart, i - fragmentStart);
1670 0 : output += ":private";
1671 0 : fragmentStart = -1;
1672 0 : } else if ((state == SINGLE_QUOTE && character == '\'') ||
1673 0 : (state == DOUBLE_QUOTE && character == '"')) {
1674 0 : if (nextCharacter == character) {
1675 : // Two consecutive quotes within a string literal are a single escaped quote
1676 0 : i++;
1677 : } else {
1678 0 : state = NORMAL;
1679 0 : fragmentStart = i + 1;
1680 : }
1681 : }
1682 0 : break;
1683 : case '-':
1684 0 : if (state == NORMAL) {
1685 0 : if (nextCharacter == '-') {
1686 0 : state = DASH_COMMENT;
1687 0 : i++;
1688 : }
1689 : }
1690 0 : break;
1691 : case '\n':
1692 0 : if (state == DASH_COMMENT) {
1693 0 : state = NORMAL;
1694 : }
1695 0 : break;
1696 : case '/':
1697 0 : if (state == NORMAL) {
1698 0 : if (nextCharacter == '*') {
1699 0 : state = C_STYLE_COMMENT;
1700 0 : i++;
1701 : }
1702 : }
1703 0 : break;
1704 : case '*':
1705 0 : if (state == C_STYLE_COMMENT) {
1706 0 : if (nextCharacter == '/') {
1707 0 : state = NORMAL;
1708 : }
1709 : }
1710 0 : break;
1711 : default:
1712 0 : continue;
1713 : }
1714 : }
1715 :
1716 0 : if ((fragmentStart >= 0) && fragmentStart < length)
1717 0 : output += nsDependentCSubstring(sql, fragmentStart, length - fragmentStart);
1718 :
1719 0 : return output;
1720 : }
1721 :
1722 : // A whitelist mechanism to prevent Telemetry reporting on Addon & Thunderbird
1723 : // DBs.
1724 : struct TrackedDBEntry
1725 : {
1726 : const char* mName;
1727 : const uint32_t mNameLength;
1728 :
1729 : // This struct isn't meant to be used beyond the static arrays below.
1730 : constexpr
1731 : TrackedDBEntry(const char* aName, uint32_t aNameLength)
1732 : : mName(aName)
1733 : , mNameLength(aNameLength)
1734 : { }
1735 :
1736 : TrackedDBEntry() = delete;
1737 : TrackedDBEntry(TrackedDBEntry&) = delete;
1738 : };
1739 :
1740 : #define TRACKEDDB_ENTRY(_name) { _name, (sizeof(_name) - 1) }
1741 :
1742 : // A whitelist of database names. If the database name exactly matches one of
1743 : // these then its SQL statements will always be recorded.
1744 : static constexpr TrackedDBEntry kTrackedDBs[] = {
1745 : // IndexedDB for about:home, see aboutHome.js
1746 : TRACKEDDB_ENTRY("818200132aebmoouht.sqlite"),
1747 : TRACKEDDB_ENTRY("addons.sqlite"),
1748 : TRACKEDDB_ENTRY("content-prefs.sqlite"),
1749 : TRACKEDDB_ENTRY("cookies.sqlite"),
1750 : TRACKEDDB_ENTRY("extensions.sqlite"),
1751 : TRACKEDDB_ENTRY("favicons.sqlite"),
1752 : TRACKEDDB_ENTRY("formhistory.sqlite"),
1753 : TRACKEDDB_ENTRY("index.sqlite"),
1754 : TRACKEDDB_ENTRY("netpredictions.sqlite"),
1755 : TRACKEDDB_ENTRY("permissions.sqlite"),
1756 : TRACKEDDB_ENTRY("places.sqlite"),
1757 : TRACKEDDB_ENTRY("reading-list.sqlite"),
1758 : TRACKEDDB_ENTRY("search.sqlite"),
1759 : TRACKEDDB_ENTRY("signons.sqlite"),
1760 : TRACKEDDB_ENTRY("urlclassifier3.sqlite"),
1761 : TRACKEDDB_ENTRY("webappsstore.sqlite")
1762 : };
1763 :
1764 : // A whitelist of database name prefixes. If the database name begins with
1765 : // one of these prefixes then its SQL statements will always be recorded.
1766 : static const TrackedDBEntry kTrackedDBPrefixes[] = {
1767 : TRACKEDDB_ENTRY("indexedDB-")
1768 : };
1769 :
1770 : #undef TRACKEDDB_ENTRY
1771 :
1772 : // Slow SQL statements will be automatically
1773 : // trimmed to kMaxSlowStatementLength characters.
1774 : // This limit doesn't include the ellipsis and DB name,
1775 : // that are appended at the end of the stored statement.
1776 : const uint32_t kMaxSlowStatementLength = 1000;
1777 :
1778 : void
1779 0 : TelemetryImpl::RecordSlowStatement(const nsACString &sql,
1780 : const nsACString &dbName,
1781 : uint32_t delay)
1782 : {
1783 0 : MOZ_ASSERT(!sql.IsEmpty());
1784 0 : MOZ_ASSERT(!dbName.IsEmpty());
1785 :
1786 0 : if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
1787 0 : return;
1788 :
1789 0 : bool recordStatement = false;
1790 :
1791 0 : for (const TrackedDBEntry& nameEntry : kTrackedDBs) {
1792 0 : MOZ_ASSERT(nameEntry.mNameLength);
1793 0 : const nsDependentCString name(nameEntry.mName, nameEntry.mNameLength);
1794 0 : if (dbName == name) {
1795 0 : recordStatement = true;
1796 0 : break;
1797 : }
1798 : }
1799 :
1800 0 : if (!recordStatement) {
1801 0 : for (const TrackedDBEntry& prefixEntry : kTrackedDBPrefixes) {
1802 0 : MOZ_ASSERT(prefixEntry.mNameLength);
1803 0 : const nsDependentCString prefix(prefixEntry.mName,
1804 0 : prefixEntry.mNameLength);
1805 0 : if (StringBeginsWith(dbName, prefix)) {
1806 0 : recordStatement = true;
1807 0 : break;
1808 : }
1809 : }
1810 : }
1811 :
1812 0 : if (recordStatement) {
1813 0 : nsAutoCString sanitizedSQL(SanitizeSQL(sql));
1814 0 : if (sanitizedSQL.Length() > kMaxSlowStatementLength) {
1815 0 : sanitizedSQL.SetLength(kMaxSlowStatementLength);
1816 0 : sanitizedSQL += "...";
1817 : }
1818 0 : sanitizedSQL.AppendPrintf(" /* %s */", nsPromiseFlatCString(dbName).get());
1819 0 : StoreSlowSQL(sanitizedSQL, delay, Sanitized);
1820 : } else {
1821 : // Report aggregate DB-level statistics for addon DBs
1822 0 : nsAutoCString aggregate;
1823 0 : aggregate.AppendPrintf("Untracked SQL for %s",
1824 0 : nsPromiseFlatCString(dbName).get());
1825 0 : StoreSlowSQL(aggregate, delay, Sanitized);
1826 : }
1827 :
1828 0 : nsAutoCString fullSQL;
1829 0 : fullSQL.AppendPrintf("%s /* %s */",
1830 0 : nsPromiseFlatCString(sql).get(),
1831 0 : nsPromiseFlatCString(dbName).get());
1832 0 : StoreSlowSQL(fullSQL, delay, Unsanitized);
1833 : }
1834 :
1835 : void
1836 0 : TelemetryImpl::RecordIceCandidates(const uint32_t iceCandidateBitmask,
1837 : const bool success)
1838 : {
1839 0 : if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
1840 0 : return;
1841 :
1842 0 : sTelemetry->mWebrtcTelemetry.RecordIceCandidateMask(iceCandidateBitmask, success);
1843 : }
1844 :
1845 : #if defined(MOZ_GECKO_PROFILER)
1846 : void
1847 0 : TelemetryImpl::RecordChromeHang(uint32_t aDuration,
1848 : Telemetry::ProcessedStack &aStack,
1849 : int32_t aSystemUptime,
1850 : int32_t aFirefoxUptime,
1851 : HangAnnotationsPtr aAnnotations)
1852 : {
1853 0 : if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
1854 0 : return;
1855 :
1856 0 : HangAnnotationsPtr annotations;
1857 : // We only pass aAnnotations if it is not empty.
1858 0 : if (aAnnotations && !aAnnotations->IsEmpty()) {
1859 0 : annotations = Move(aAnnotations);
1860 : }
1861 :
1862 0 : MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex);
1863 :
1864 0 : sTelemetry->mHangReports.AddHang(aStack, aDuration,
1865 : aSystemUptime, aFirefoxUptime,
1866 0 : Move(annotations));
1867 : }
1868 :
1869 : void
1870 0 : TelemetryImpl::DoStackCapture(const nsACString& aKey) {
1871 0 : if (Telemetry::CanRecordExtended() && XRE_IsParentProcess()) {
1872 0 : sTelemetry->mStackCapturer.Capture(aKey);
1873 : }
1874 0 : }
1875 : #endif
1876 :
1877 : nsresult
1878 0 : TelemetryImpl::CaptureStack(const nsACString& aKey) {
1879 : #if defined(MOZ_GECKO_PROFILER)
1880 0 : TelemetryImpl::DoStackCapture(aKey);
1881 : #endif
1882 0 : return NS_OK;
1883 : }
1884 :
1885 : void
1886 0 : TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats&& aStats)
1887 : {
1888 0 : if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
1889 0 : return;
1890 :
1891 0 : MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex);
1892 :
1893 : // Ignore OOM.
1894 0 : mozilla::Unused << sTelemetry->mThreadHangStats.append(Move(aStats));
1895 : }
1896 :
1897 : bool
1898 140 : TelemetryImpl::CanRecordBase()
1899 : {
1900 140 : if (!sTelemetry) {
1901 0 : return false;
1902 : }
1903 : bool canRecordBase;
1904 140 : nsresult rv = sTelemetry->GetCanRecordBase(&canRecordBase);
1905 140 : return NS_SUCCEEDED(rv) && canRecordBase;
1906 : }
1907 :
1908 : bool
1909 46 : TelemetryImpl::CanRecordExtended()
1910 : {
1911 46 : if (!sTelemetry) {
1912 0 : return false;
1913 : }
1914 : bool canRecordExtended;
1915 46 : nsresult rv = sTelemetry->GetCanRecordExtended(&canRecordExtended);
1916 46 : return NS_SUCCEEDED(rv) && canRecordExtended;
1917 : }
1918 :
1919 547 : NS_IMPL_ISUPPORTS(TelemetryImpl, nsITelemetry, nsIMemoryReporter)
1920 3 : NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
1921 :
1922 : #define NS_TELEMETRY_CID \
1923 : {0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}}
1924 : NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID);
1925 :
1926 : const Module::CIDEntry kTelemetryCIDs[] = {
1927 : { &kNS_TELEMETRY_CID, false, nullptr, nsITelemetryConstructor, Module::ALLOW_IN_GPU_PROCESS },
1928 : { nullptr }
1929 : };
1930 :
1931 : const Module::ContractIDEntry kTelemetryContracts[] = {
1932 : { "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID, Module::ALLOW_IN_GPU_PROCESS },
1933 : { nullptr }
1934 : };
1935 :
1936 : const Module kTelemetryModule = {
1937 : Module::kVersion,
1938 : kTelemetryCIDs,
1939 : kTelemetryContracts,
1940 : nullptr,
1941 : nullptr,
1942 : nullptr,
1943 : TelemetryImpl::ShutdownTelemetry,
1944 : Module::ALLOW_IN_GPU_PROCESS
1945 : };
1946 :
1947 : NS_IMETHODIMP
1948 0 : TelemetryImpl::GetFileIOReports(JSContext *cx, JS::MutableHandleValue ret)
1949 : {
1950 0 : if (sTelemetryIOObserver) {
1951 0 : JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
1952 0 : if (!obj) {
1953 0 : return NS_ERROR_FAILURE;
1954 : }
1955 :
1956 0 : if (!sTelemetryIOObserver->ReflectIntoJS(cx, obj)) {
1957 0 : return NS_ERROR_FAILURE;
1958 : }
1959 0 : ret.setObject(*obj);
1960 0 : return NS_OK;
1961 : }
1962 0 : ret.setNull();
1963 0 : return NS_OK;
1964 : }
1965 :
1966 : NS_IMETHODIMP
1967 6 : TelemetryImpl::MsSinceProcessStart(double* aResult)
1968 : {
1969 6 : return Telemetry::Common::MsSinceProcessStart(aResult);
1970 : }
1971 :
1972 : NS_IMETHODIMP
1973 2 : TelemetryImpl::MsSystemNow(double* aResult)
1974 : {
1975 : #ifdef XP_LINUX
1976 : timespec ts;
1977 2 : clock_gettime(CLOCK_REALTIME, &ts);
1978 2 : *aResult = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
1979 : #else
1980 : using namespace std::chrono;
1981 : milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
1982 : *aResult = static_cast<double>(ms.count());
1983 : #endif // XP_LINUX
1984 :
1985 2 : return NS_OK;
1986 : }
1987 :
1988 : // Telemetry Scalars IDL Implementation
1989 :
1990 : NS_IMETHODIMP
1991 0 : TelemetryImpl::ScalarAdd(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
1992 : {
1993 0 : return TelemetryScalar::Add(aName, aVal, aCx);
1994 : }
1995 :
1996 : NS_IMETHODIMP
1997 0 : TelemetryImpl::ScalarSet(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
1998 : {
1999 0 : return TelemetryScalar::Set(aName, aVal, aCx);
2000 : }
2001 :
2002 : NS_IMETHODIMP
2003 0 : TelemetryImpl::ScalarSetMaximum(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
2004 : {
2005 0 : return TelemetryScalar::SetMaximum(aName, aVal, aCx);
2006 : }
2007 :
2008 : NS_IMETHODIMP
2009 0 : TelemetryImpl::SnapshotScalars(unsigned int aDataset, bool aClearScalars, JSContext* aCx,
2010 : uint8_t optional_argc, JS::MutableHandleValue aResult)
2011 : {
2012 0 : return TelemetryScalar::CreateSnapshots(aDataset, aClearScalars, aCx, optional_argc, aResult);
2013 : }
2014 :
2015 : NS_IMETHODIMP
2016 0 : TelemetryImpl::KeyedScalarAdd(const nsACString& aName, const nsAString& aKey,
2017 : JS::HandleValue aVal, JSContext* aCx)
2018 : {
2019 0 : return TelemetryScalar::Add(aName, aKey, aVal, aCx);
2020 : }
2021 :
2022 : NS_IMETHODIMP
2023 0 : TelemetryImpl::KeyedScalarSet(const nsACString& aName, const nsAString& aKey,
2024 : JS::HandleValue aVal, JSContext* aCx)
2025 : {
2026 0 : return TelemetryScalar::Set(aName, aKey, aVal, aCx);
2027 : }
2028 :
2029 : NS_IMETHODIMP
2030 0 : TelemetryImpl::KeyedScalarSetMaximum(const nsACString& aName, const nsAString& aKey,
2031 : JS::HandleValue aVal, JSContext* aCx)
2032 : {
2033 0 : return TelemetryScalar::SetMaximum(aName, aKey, aVal, aCx);
2034 : }
2035 :
2036 : NS_IMETHODIMP
2037 0 : TelemetryImpl::SnapshotKeyedScalars(unsigned int aDataset, bool aClearScalars, JSContext* aCx,
2038 : uint8_t optional_argc, JS::MutableHandleValue aResult)
2039 : {
2040 0 : return TelemetryScalar::CreateKeyedSnapshots(aDataset, aClearScalars, aCx, optional_argc,
2041 0 : aResult);
2042 : }
2043 :
2044 : NS_IMETHODIMP
2045 0 : TelemetryImpl::ClearScalars()
2046 : {
2047 0 : TelemetryScalar::ClearScalars();
2048 0 : return NS_OK;
2049 : }
2050 :
2051 : // Telemetry Event IDL implementation.
2052 :
2053 : NS_IMETHODIMP
2054 0 : TelemetryImpl::RecordEvent(const nsACString & aCategory, const nsACString & aMethod,
2055 : const nsACString & aObject, JS::HandleValue aValue,
2056 : JS::HandleValue aExtra, JSContext* aCx, uint8_t optional_argc)
2057 : {
2058 0 : return TelemetryEvent::RecordEvent(aCategory, aMethod, aObject, aValue, aExtra, aCx, optional_argc);
2059 : }
2060 :
2061 : NS_IMETHODIMP
2062 0 : TelemetryImpl::SnapshotBuiltinEvents(uint32_t aDataset, bool aClear, JSContext* aCx,
2063 : uint8_t optional_argc, JS::MutableHandleValue aResult)
2064 : {
2065 0 : return TelemetryEvent::CreateSnapshots(aDataset, aClear, aCx, optional_argc, aResult);
2066 : }
2067 :
2068 : NS_IMETHODIMP
2069 0 : TelemetryImpl::ClearEvents()
2070 : {
2071 0 : TelemetryEvent::ClearEvents();
2072 0 : return NS_OK;
2073 : }
2074 :
2075 : NS_IMETHODIMP
2076 0 : TelemetryImpl::SetEventRecordingEnabled(const nsACString& aCategory, bool aEnabled)
2077 : {
2078 0 : TelemetryEvent::SetEventRecordingEnabled(aCategory, aEnabled);
2079 0 : return NS_OK;
2080 : }
2081 :
2082 : NS_IMETHODIMP
2083 0 : TelemetryImpl::FlushBatchedChildTelemetry()
2084 : {
2085 0 : TelemetryIPCAccumulator::IPCTimerFired(nullptr, nullptr);
2086 0 : return NS_OK;
2087 : }
2088 :
2089 : size_t
2090 0 : TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
2091 : {
2092 0 : size_t n = aMallocSizeOf(this);
2093 :
2094 0 : n += TelemetryHistogram::GetMapShallowSizesOfExcludingThis(aMallocSizeOf);
2095 0 : n += TelemetryScalar::GetMapShallowSizesOfExcludingThis(aMallocSizeOf);
2096 0 : n += mWebrtcTelemetry.SizeOfExcludingThis(aMallocSizeOf);
2097 : { // Scope for mHashMutex lock
2098 0 : MutexAutoLock lock(mHashMutex);
2099 0 : n += mPrivateSQL.SizeOfExcludingThis(aMallocSizeOf);
2100 0 : n += mSanitizedSQL.SizeOfExcludingThis(aMallocSizeOf);
2101 : }
2102 : { // Scope for mHangReportsMutex lock
2103 0 : MutexAutoLock lock(mHangReportsMutex);
2104 0 : n += mHangReports.SizeOfExcludingThis(aMallocSizeOf);
2105 : }
2106 : { // Scope for mThreadHangStatsMutex lock
2107 0 : MutexAutoLock lock(mThreadHangStatsMutex);
2108 0 : n += mThreadHangStats.sizeOfExcludingThis(aMallocSizeOf);
2109 : }
2110 :
2111 : // It's a bit gross that we measure this other stuff that lives outside of
2112 : // TelemetryImpl... oh well.
2113 0 : if (sTelemetryIOObserver) {
2114 0 : n += sTelemetryIOObserver->SizeOfIncludingThis(aMallocSizeOf);
2115 : }
2116 :
2117 0 : n += TelemetryHistogram::GetHistogramSizesofIncludingThis(aMallocSizeOf);
2118 0 : n += TelemetryScalar::GetScalarSizesOfIncludingThis(aMallocSizeOf);
2119 0 : n += TelemetryEvent::SizeOfIncludingThis(aMallocSizeOf);
2120 :
2121 0 : return n;
2122 : }
2123 :
2124 : } // namespace
2125 :
2126 :
2127 : ////////////////////////////////////////////////////////////////////////
2128 : ////////////////////////////////////////////////////////////////////////
2129 : //
2130 : // EXTERNALLY VISIBLE FUNCTIONS in no name space
2131 : // These are NOT listed in Telemetry.h
2132 :
2133 : NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule;
2134 :
2135 : /**
2136 : * The XRE_TelemetryAdd function is to be used by embedding applications
2137 : * that can't use mozilla::Telemetry::Accumulate() directly.
2138 : */
2139 : void
2140 0 : XRE_TelemetryAccumulate(int aID, uint32_t aSample)
2141 : {
2142 0 : mozilla::Telemetry::Accumulate((mozilla::Telemetry::HistogramID) aID, aSample);
2143 0 : }
2144 :
2145 :
2146 : ////////////////////////////////////////////////////////////////////////
2147 : ////////////////////////////////////////////////////////////////////////
2148 : //
2149 : // EXTERNALLY VISIBLE FUNCTIONS in mozilla::
2150 : // These are NOT listed in Telemetry.h
2151 :
2152 : namespace mozilla {
2153 :
2154 : void
2155 0 : RecordShutdownStartTimeStamp() {
2156 : #ifdef DEBUG
2157 : // FIXME: this function should only be called once, since it should be called
2158 : // at the earliest point we *know* we are shutting down. Unfortunately
2159 : // this assert has been firing. Given that if we are called multiple times
2160 : // we just keep the last timestamp, the assert is commented for now.
2161 : static bool recorded = false;
2162 : // MOZ_ASSERT(!recorded);
2163 : (void)recorded; // Silence unused-var warnings (remove when assert re-enabled)
2164 0 : recorded = true;
2165 : #endif
2166 :
2167 0 : if (!Telemetry::CanRecordExtended())
2168 0 : return;
2169 :
2170 0 : gRecordedShutdownStartTime = TimeStamp::Now();
2171 :
2172 0 : GetShutdownTimeFileName();
2173 : }
2174 :
2175 : void
2176 0 : RecordShutdownEndTimeStamp() {
2177 0 : if (!gRecordedShutdownTimeFileName || gAlreadyFreedShutdownTimeFileName)
2178 0 : return;
2179 :
2180 0 : nsCString name(gRecordedShutdownTimeFileName);
2181 0 : PL_strfree(gRecordedShutdownTimeFileName);
2182 0 : gRecordedShutdownTimeFileName = nullptr;
2183 0 : gAlreadyFreedShutdownTimeFileName = true;
2184 :
2185 0 : if (gRecordedShutdownStartTime.IsNull()) {
2186 : // If |CanRecordExtended()| is true before |AsyncFetchTelemetryData| is called and
2187 : // then disabled before shutdown, |RecordShutdownStartTimeStamp| will bail out and
2188 : // we will end up with a null |gRecordedShutdownStartTime| here. This can happen
2189 : // during tests.
2190 0 : return;
2191 : }
2192 :
2193 0 : nsCString tmpName = name;
2194 0 : tmpName += ".tmp";
2195 0 : FILE *f = fopen(tmpName.get(), "w");
2196 0 : if (!f)
2197 0 : return;
2198 : // On a normal release build this should be called just before
2199 : // calling _exit, but on a debug build or when the user forces a full
2200 : // shutdown this is called as late as possible, so we have to
2201 : // white list this write as write poisoning will be enabled.
2202 0 : MozillaRegisterDebugFILE(f);
2203 :
2204 0 : TimeStamp now = TimeStamp::Now();
2205 0 : MOZ_ASSERT(now >= gRecordedShutdownStartTime);
2206 0 : TimeDuration diff = now - gRecordedShutdownStartTime;
2207 0 : uint32_t diff2 = diff.ToMilliseconds();
2208 0 : int written = fprintf(f, "%d\n", diff2);
2209 0 : MozillaUnRegisterDebugFILE(f);
2210 0 : int rv = fclose(f);
2211 0 : if (written < 0 || rv != 0) {
2212 0 : PR_Delete(tmpName.get());
2213 0 : return;
2214 : }
2215 0 : PR_Delete(name.get());
2216 0 : PR_Rename(tmpName.get(), name.get());
2217 : }
2218 :
2219 : } // namespace mozilla
2220 :
2221 :
2222 : ////////////////////////////////////////////////////////////////////////
2223 : ////////////////////////////////////////////////////////////////////////
2224 : //
2225 : // EXTERNALLY VISIBLE FUNCTIONS in mozilla::Telemetry::
2226 : // These are listed in Telemetry.h
2227 :
2228 : namespace mozilla {
2229 : namespace Telemetry {
2230 :
2231 : // The external API for controlling recording state
2232 : void
2233 0 : SetHistogramRecordingEnabled(HistogramID aID, bool aEnabled)
2234 : {
2235 0 : TelemetryHistogram::SetHistogramRecordingEnabled(aID, aEnabled);
2236 0 : }
2237 :
2238 : void
2239 2784 : Accumulate(HistogramID aHistogram, uint32_t aSample)
2240 : {
2241 2784 : TelemetryHistogram::Accumulate(aHistogram, aSample);
2242 2784 : }
2243 :
2244 : void
2245 1284 : Accumulate(HistogramID aID, const nsCString& aKey, uint32_t aSample)
2246 : {
2247 1284 : TelemetryHistogram::Accumulate(aID, aKey, aSample);
2248 1284 : }
2249 :
2250 : void
2251 0 : Accumulate(const char* name, uint32_t sample)
2252 : {
2253 0 : TelemetryHistogram::Accumulate(name, sample);
2254 0 : }
2255 :
2256 : void
2257 0 : Accumulate(const char *name, const nsCString& key, uint32_t sample)
2258 : {
2259 0 : TelemetryHistogram::Accumulate(name, key, sample);
2260 0 : }
2261 :
2262 : void
2263 0 : AccumulateCategorical(HistogramID id, const nsCString& label)
2264 : {
2265 0 : TelemetryHistogram::AccumulateCategorical(id, label);
2266 0 : }
2267 :
2268 : void
2269 334 : AccumulateTimeDelta(HistogramID aHistogram, TimeStamp start, TimeStamp end)
2270 : {
2271 334 : Accumulate(aHistogram,
2272 668 : static_cast<uint32_t>((end - start).ToMilliseconds()));
2273 334 : }
2274 :
2275 : const char*
2276 0 : GetHistogramName(HistogramID id)
2277 : {
2278 0 : return TelemetryHistogram::GetHistogramName(id);
2279 : }
2280 :
2281 : bool
2282 140 : CanRecordBase()
2283 : {
2284 140 : return TelemetryImpl::CanRecordBase();
2285 : }
2286 :
2287 : bool
2288 46 : CanRecordExtended()
2289 : {
2290 46 : return TelemetryImpl::CanRecordExtended();
2291 : }
2292 :
2293 : void
2294 0 : RecordSlowSQLStatement(const nsACString &statement,
2295 : const nsACString &dbName,
2296 : uint32_t delay)
2297 : {
2298 0 : TelemetryImpl::RecordSlowStatement(statement, dbName, delay);
2299 0 : }
2300 :
2301 : void
2302 0 : RecordWebrtcIceCandidates(const uint32_t iceCandidateBitmask,
2303 : const bool success)
2304 : {
2305 0 : TelemetryImpl::RecordIceCandidates(iceCandidateBitmask, success);
2306 0 : }
2307 :
2308 3 : void Init()
2309 : {
2310 : // Make the service manager hold a long-lived reference to the service
2311 : nsCOMPtr<nsITelemetry> telemetryService =
2312 6 : do_GetService("@mozilla.org/base/telemetry;1");
2313 3 : MOZ_ASSERT(telemetryService);
2314 3 : }
2315 :
2316 : #if defined(MOZ_GECKO_PROFILER)
2317 0 : void RecordChromeHang(uint32_t duration,
2318 : ProcessedStack &aStack,
2319 : int32_t aSystemUptime,
2320 : int32_t aFirefoxUptime,
2321 : HangAnnotationsPtr aAnnotations)
2322 : {
2323 0 : TelemetryImpl::RecordChromeHang(duration, aStack,
2324 : aSystemUptime, aFirefoxUptime,
2325 0 : Move(aAnnotations));
2326 0 : }
2327 :
2328 0 : void CaptureStack(const nsACString& aKey)
2329 : {
2330 : #if defined(MOZ_GECKO_PROFILER)
2331 0 : TelemetryImpl::DoStackCapture(aKey);
2332 : #endif
2333 0 : }
2334 : #endif
2335 :
2336 0 : void RecordThreadHangStats(ThreadHangStats&& aStats)
2337 : {
2338 0 : TelemetryImpl::RecordThreadHangStats(Move(aStats));
2339 0 : }
2340 :
2341 :
2342 : void
2343 0 : WriteFailedProfileLock(nsIFile* aProfileDir)
2344 : {
2345 0 : nsCOMPtr<nsIFile> file;
2346 0 : nsresult rv = GetFailedProfileLockFile(getter_AddRefs(file), aProfileDir);
2347 0 : NS_ENSURE_SUCCESS_VOID(rv);
2348 0 : int64_t fileSize = 0;
2349 0 : rv = file->GetFileSize(&fileSize);
2350 : // It's expected that the file might not exist yet
2351 0 : if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
2352 0 : return;
2353 : }
2354 0 : nsCOMPtr<nsIFileStream> fileStream;
2355 0 : rv = NS_NewLocalFileStream(getter_AddRefs(fileStream), file,
2356 0 : PR_RDWR | PR_CREATE_FILE, 0640);
2357 0 : NS_ENSURE_SUCCESS_VOID(rv);
2358 0 : NS_ENSURE_TRUE_VOID(fileSize <= kMaxFailedProfileLockFileSize);
2359 0 : unsigned int failedLockCount = 0;
2360 0 : if (fileSize > 0) {
2361 0 : nsCOMPtr<nsIInputStream> inStream = do_QueryInterface(fileStream);
2362 0 : NS_ENSURE_TRUE_VOID(inStream);
2363 0 : if (!GetFailedLockCount(inStream, fileSize, failedLockCount)) {
2364 0 : failedLockCount = 0;
2365 : }
2366 : }
2367 0 : ++failedLockCount;
2368 0 : nsAutoCString bufStr;
2369 0 : bufStr.AppendInt(static_cast<int>(failedLockCount));
2370 0 : nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(fileStream);
2371 0 : NS_ENSURE_TRUE_VOID(seekStream);
2372 : // If we read in an existing failed lock count, we need to reset the file ptr
2373 0 : if (fileSize > 0) {
2374 0 : rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
2375 0 : NS_ENSURE_SUCCESS_VOID(rv);
2376 : }
2377 0 : nsCOMPtr<nsIOutputStream> outStream = do_QueryInterface(fileStream);
2378 0 : uint32_t bytesLeft = bufStr.Length();
2379 0 : const char* bytes = bufStr.get();
2380 0 : do {
2381 0 : uint32_t written = 0;
2382 0 : rv = outStream->Write(bytes, bytesLeft, &written);
2383 0 : if (NS_FAILED(rv)) {
2384 0 : break;
2385 : }
2386 0 : bytes += written;
2387 0 : bytesLeft -= written;
2388 0 : } while (bytesLeft > 0);
2389 0 : seekStream->SetEOF();
2390 : }
2391 :
2392 : void
2393 1 : InitIOReporting(nsIFile* aXreDir)
2394 : {
2395 : // Never initialize twice
2396 1 : if (sTelemetryIOObserver) {
2397 0 : return;
2398 : }
2399 :
2400 1 : sTelemetryIOObserver = new TelemetryIOInterposeObserver(aXreDir);
2401 : IOInterposer::Register(IOInterposeObserver::OpAllWithStaging,
2402 1 : sTelemetryIOObserver);
2403 : }
2404 :
2405 : void
2406 1 : SetProfileDir(nsIFile* aProfD)
2407 : {
2408 1 : if (!sTelemetryIOObserver || !aProfD) {
2409 0 : return;
2410 : }
2411 2 : nsAutoString profDirPath;
2412 1 : nsresult rv = aProfD->GetPath(profDirPath);
2413 1 : if (NS_FAILED(rv)) {
2414 0 : return;
2415 : }
2416 1 : sTelemetryIOObserver->AddPath(profDirPath, NS_LITERAL_STRING("{profile}"));
2417 : }
2418 :
2419 3 : void CreateStatisticsRecorder()
2420 : {
2421 3 : TelemetryHistogram::CreateStatisticsRecorder();
2422 3 : }
2423 :
2424 0 : void DestroyStatisticsRecorder()
2425 : {
2426 0 : TelemetryHistogram::DestroyStatisticsRecorder();
2427 0 : }
2428 :
2429 : // Scalar API C++ Endpoints
2430 :
2431 : void
2432 0 : ScalarAdd(mozilla::Telemetry::ScalarID aId, uint32_t aVal)
2433 : {
2434 0 : TelemetryScalar::Add(aId, aVal);
2435 0 : }
2436 :
2437 : void
2438 0 : ScalarSet(mozilla::Telemetry::ScalarID aId, uint32_t aVal)
2439 : {
2440 0 : TelemetryScalar::Set(aId, aVal);
2441 0 : }
2442 :
2443 : void
2444 1 : ScalarSet(mozilla::Telemetry::ScalarID aId, bool aVal)
2445 : {
2446 1 : TelemetryScalar::Set(aId, aVal);
2447 1 : }
2448 :
2449 : void
2450 0 : ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aVal)
2451 : {
2452 0 : TelemetryScalar::Set(aId, aVal);
2453 0 : }
2454 :
2455 : void
2456 0 : ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, uint32_t aVal)
2457 : {
2458 0 : TelemetryScalar::SetMaximum(aId, aVal);
2459 0 : }
2460 :
2461 : void
2462 0 : ScalarAdd(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aVal)
2463 : {
2464 0 : TelemetryScalar::Add(aId, aKey, aVal);
2465 0 : }
2466 :
2467 : void
2468 0 : ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aVal)
2469 : {
2470 0 : TelemetryScalar::Set(aId, aKey, aVal);
2471 0 : }
2472 :
2473 : void
2474 2 : ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, bool aVal)
2475 : {
2476 2 : TelemetryScalar::Set(aId, aKey, aVal);
2477 2 : }
2478 :
2479 : void
2480 0 : ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aVal)
2481 : {
2482 0 : TelemetryScalar::SetMaximum(aId, aKey, aVal);
2483 0 : }
2484 :
2485 : } // namespace Telemetry
2486 9 : } // namespace mozilla
|