LCOV - code coverage report
Current view: top level - toolkit/components/telemetry - Telemetry.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 127 1013 12.5 %
Date: 2017-07-14 16:53:18 Functions: 38 143 26.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <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

Generated by: LCOV version 1.13