LCOV - code coverage report
Current view: top level - toolkit/components/perfmonitoring - nsPerformanceStats.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 616 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 133 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "nsPerformanceStats.h"
       6             : 
       7             : #include "nsMemory.h"
       8             : #include "nsLiteralString.h"
       9             : #include "nsCRTGlue.h"
      10             : #include "nsServiceManagerUtils.h"
      11             : 
      12             : #include "nsCOMArray.h"
      13             : #include "nsContentUtils.h"
      14             : #include "nsIMutableArray.h"
      15             : #include "nsReadableUtils.h"
      16             : 
      17             : #include "jsapi.h"
      18             : #include "nsJSUtils.h"
      19             : #include "xpcpublic.h"
      20             : #include "jspubtd.h"
      21             : 
      22             : #include "nsIDOMWindow.h"
      23             : #include "nsGlobalWindow.h"
      24             : #include "nsRefreshDriver.h"
      25             : #include "nsThreadUtils.h"
      26             : 
      27             : #include "mozilla/Unused.h"
      28             : #include "mozilla/ArrayUtils.h"
      29             : #include "mozilla/dom/ScriptSettings.h"
      30             : #include "mozilla/EventStateManager.h"
      31             : #include "mozilla/Services.h"
      32             : #include "mozilla/Telemetry.h"
      33             : 
      34             : #if defined(XP_WIN)
      35             : #include <processthreadsapi.h>
      36             : #include <windows.h>
      37             : #else
      38             : #include <unistd.h>
      39             : #endif // defined(XP_WIN)
      40             : 
      41             : #if defined(XP_MACOSX)
      42             : #include <mach/mach_init.h>
      43             : #include <mach/mach_interface.h>
      44             : #include <mach/mach_port.h>
      45             : #include <mach/mach_types.h>
      46             : #include <mach/message.h>
      47             : #include <mach/thread_info.h>
      48             : #elif defined(XP_UNIX)
      49             : #include <sys/time.h>
      50             : #include <sys/resource.h>
      51             : #endif // defined(XP_UNIX)
      52             : /* ------------------------------------------------------
      53             :  *
      54             :  * Utility functions.
      55             :  *
      56             :  */
      57             : 
      58             : namespace {
      59             : 
      60             : /**
      61             :  * Get the private window for the current compartment.
      62             :  *
      63             :  * @return null if the code is not executed in a window or in
      64             :  * case of error, a nsPIDOMWindow otherwise.
      65             :  */
      66             : already_AddRefed<nsPIDOMWindowOuter>
      67           0 : GetPrivateWindow(JSContext* cx) {
      68           0 :   nsGlobalWindow* win = xpc::CurrentWindowOrNull(cx);
      69           0 :   if (!win) {
      70           0 :     return nullptr;
      71             :   }
      72             : 
      73           0 :   nsPIDOMWindowOuter* outer = win->AsInner()->GetOuterWindow();
      74           0 :   if (!outer) {
      75           0 :     return nullptr;
      76             :   }
      77             : 
      78           0 :   nsCOMPtr<nsPIDOMWindowOuter> top = outer->GetTop();
      79           0 :   if (!top) {
      80           0 :     return nullptr;
      81             :   }
      82             : 
      83           0 :   return top.forget();
      84             : }
      85             : 
      86             : bool
      87           0 : URLForGlobal(JSContext* cx, JS::Handle<JSObject*> global, nsAString& url) {
      88           0 :   nsCOMPtr<nsIPrincipal> principal = nsContentUtils::ObjectPrincipal(global);
      89           0 :   if (!principal) {
      90           0 :     return false;
      91             :   }
      92             : 
      93           0 :   nsCOMPtr<nsIURI> uri;
      94           0 :   nsresult rv = principal->GetURI(getter_AddRefs(uri));
      95           0 :   if (NS_FAILED(rv) || !uri) {
      96           0 :     return false;
      97             :   }
      98             : 
      99           0 :   nsAutoCString spec;
     100           0 :   rv = uri->GetSpec(spec);
     101           0 :   if (NS_FAILED(rv)) {
     102           0 :     return false;
     103             :   }
     104             : 
     105           0 :   url.Assign(NS_ConvertUTF8toUTF16(spec));
     106           0 :   return true;
     107             : }
     108             : 
     109             : /**
     110             :  * Extract a somewhat human-readable name from the current context.
     111             :  */
     112             : void
     113           0 : CompartmentName(JSContext* cx, JS::Handle<JSObject*> global, nsAString& name) {
     114             :   // Attempt to use the URL as name.
     115           0 :   if (URLForGlobal(cx, global, name)) {
     116           0 :     return;
     117             :   }
     118             : 
     119             :   // Otherwise, fallback to XPConnect's less readable but more
     120             :   // complete naming scheme.
     121           0 :   nsAutoCString cname;
     122           0 :   xpc::GetCurrentCompartmentName(cx, cname);
     123           0 :   name.Assign(NS_ConvertUTF8toUTF16(cname));
     124             : }
     125             : 
     126             : /**
     127             :  * Generate a unique-to-the-application identifier for a group.
     128             :  */
     129             : void
     130           0 : GenerateUniqueGroupId(uint64_t uid, uint64_t processId, nsAString& groupId)
     131             : {
     132           0 :   uint64_t threadId = reinterpret_cast<uint64_t>(mozilla::GetCurrentPhysicalThread());
     133             : 
     134           0 :   groupId.AssignLiteral("process: ");
     135           0 :   groupId.AppendInt(processId);
     136           0 :   groupId.AppendLiteral(", thread: ");
     137           0 :   groupId.AppendInt(threadId);
     138           0 :   groupId.AppendLiteral(", group: ");
     139           0 :   groupId.AppendInt(uid);
     140           0 : }
     141             : 
     142             : static const char* TOPICS[] = {
     143             :   "profile-before-change",
     144             :   "quit-application",
     145             :   "quit-application-granted",
     146             :   "xpcom-will-shutdown"
     147             : };
     148             : 
     149             : } // namespace
     150             : 
     151             : /* ------------------------------------------------------
     152             :  *
     153             :  * class nsPerformanceObservationTarget
     154             :  *
     155             :  */
     156             : 
     157             : 
     158           0 : NS_IMPL_ISUPPORTS(nsPerformanceObservationTarget, nsIPerformanceObservable)
     159             : 
     160             : 
     161             : 
     162             : NS_IMETHODIMP
     163           0 : nsPerformanceObservationTarget::GetTarget(nsIPerformanceGroupDetails** _result) {
     164           0 :   if (mDetails) {
     165           0 :     NS_IF_ADDREF(*_result = mDetails);
     166             :   }
     167           0 :   return NS_OK;
     168             : };
     169             : 
     170             : void
     171           0 : nsPerformanceObservationTarget::SetTarget(nsPerformanceGroupDetails* details) {
     172           0 :   MOZ_ASSERT(!mDetails);
     173           0 :   mDetails = details;
     174           0 : };
     175             : 
     176             : NS_IMETHODIMP
     177           0 : nsPerformanceObservationTarget::AddJankObserver(nsIPerformanceObserver* observer) {
     178           0 :   if (!mObservers.append(observer)) {
     179           0 :     MOZ_CRASH();
     180             :   }
     181           0 :   return NS_OK;
     182             : };
     183             : 
     184             : NS_IMETHODIMP
     185           0 : nsPerformanceObservationTarget::RemoveJankObserver(nsIPerformanceObserver* observer) {
     186           0 :   for (auto iter = mObservers.begin(), end = mObservers.end(); iter < end; ++iter) {
     187           0 :     if (*iter == observer) {
     188           0 :       mObservers.erase(iter);
     189           0 :       return NS_OK;
     190             :     }
     191             :   }
     192           0 :   return NS_OK;
     193             : };
     194             : 
     195             : bool
     196           0 : nsPerformanceObservationTarget::HasObservers() const {
     197           0 :   return !mObservers.empty();
     198             : }
     199             : 
     200             : void
     201           0 : nsPerformanceObservationTarget::NotifyJankObservers(nsIPerformanceGroupDetails* source, nsIPerformanceAlert* gravity) {
     202             :   // Copy the vector to make sure that it won't change under our feet.
     203           0 :   mozilla::Vector<nsCOMPtr<nsIPerformanceObserver>> observers;
     204           0 :   if (!observers.appendAll(mObservers)) {
     205           0 :     MOZ_CRASH();
     206             :   }
     207             : 
     208             :   // Now actually notify.
     209           0 :   for (auto iter = observers.begin(), end = observers.end(); iter < end; ++iter) {
     210           0 :     nsCOMPtr<nsIPerformanceObserver> observer = *iter;
     211           0 :     mozilla::Unused << observer->Observe(source, gravity);
     212             :   }
     213           0 : }
     214             : 
     215             : /* ------------------------------------------------------
     216             :  *
     217             :  * class nsGroupHolder
     218             :  *
     219             :  */
     220             : 
     221             : nsPerformanceObservationTarget*
     222           0 : nsGroupHolder::ObservationTarget() {
     223           0 :   if (!mPendingObservationTarget) {
     224           0 :     mPendingObservationTarget = new nsPerformanceObservationTarget();
     225             :   }
     226           0 :   return mPendingObservationTarget;
     227             : }
     228             : 
     229             : nsPerformanceGroup*
     230           0 : nsGroupHolder::GetGroup() {
     231           0 :   return mGroup;
     232             : }
     233             : 
     234             : void
     235           0 : nsGroupHolder::SetGroup(nsPerformanceGroup* group) {
     236           0 :   MOZ_ASSERT(!mGroup);
     237           0 :   mGroup = group;
     238           0 :   group->SetObservationTarget(ObservationTarget());
     239           0 :   mPendingObservationTarget->SetTarget(group->Details());
     240           0 : }
     241             : 
     242             : /* ------------------------------------------------------
     243             :  *
     244             :  * struct PerformanceData
     245             :  *
     246             :  */
     247             : 
     248           0 : PerformanceData::PerformanceData()
     249             :   : mTotalUserTime(0)
     250             :   , mTotalSystemTime(0)
     251             :   , mTotalCPOWTime(0)
     252           0 :   , mTicks(0)
     253             : {
     254           0 :   mozilla::PodArrayZero(mDurations);
     255           0 : }
     256             : 
     257             : /* ------------------------------------------------------
     258             :  *
     259             :  * class nsPerformanceGroupDetails
     260             :  *
     261             :  */
     262             : 
     263           0 : NS_IMPL_ISUPPORTS(nsPerformanceGroupDetails, nsIPerformanceGroupDetails)
     264             : 
     265             : const nsAString&
     266           0 : nsPerformanceGroupDetails::Name() const {
     267           0 :   return mName;
     268             : }
     269             : 
     270             : const nsAString&
     271           0 : nsPerformanceGroupDetails::GroupId() const {
     272           0 :   return mGroupId;
     273             : }
     274             : 
     275             : uint64_t
     276           0 : nsPerformanceGroupDetails::WindowId() const {
     277           0 :   return mWindowId;
     278             : }
     279             : 
     280             : uint64_t
     281           0 : nsPerformanceGroupDetails::ProcessId() const {
     282           0 :   return mProcessId;
     283             : }
     284             : 
     285             : bool
     286           0 : nsPerformanceGroupDetails::IsSystem() const {
     287           0 :   return mIsSystem;
     288             : }
     289             : 
     290             : bool
     291           0 : nsPerformanceGroupDetails::IsWindow() const {
     292           0 :   return mWindowId != 0;
     293             : }
     294             : 
     295             : bool
     296           0 : nsPerformanceGroupDetails::IsContentProcess() const {
     297           0 :   return XRE_GetProcessType() == GeckoProcessType_Content;
     298             : }
     299             : 
     300             : /* readonly attribute AString name; */
     301             : NS_IMETHODIMP
     302           0 : nsPerformanceGroupDetails::GetName(nsAString& aName) {
     303           0 :   aName.Assign(Name());
     304           0 :   return NS_OK;
     305             : };
     306             : 
     307             : /* readonly attribute AString groupId; */
     308             : NS_IMETHODIMP
     309           0 : nsPerformanceGroupDetails::GetGroupId(nsAString& aGroupId) {
     310           0 :   aGroupId.Assign(GroupId());
     311           0 :   return NS_OK;
     312             : };
     313             : 
     314             : /* readonly attribute uint64_t windowId; */
     315             : NS_IMETHODIMP
     316           0 : nsPerformanceGroupDetails::GetWindowId(uint64_t *aWindowId) {
     317           0 :   *aWindowId = WindowId();
     318           0 :   return NS_OK;
     319             : }
     320             : 
     321             : /* readonly attribute bool isSystem; */
     322             : NS_IMETHODIMP
     323           0 : nsPerformanceGroupDetails::GetIsSystem(bool *_retval) {
     324           0 :   *_retval = IsSystem();
     325           0 :   return NS_OK;
     326             : }
     327             : 
     328             : /*
     329             :   readonly attribute unsigned long long processId;
     330             : */
     331             : NS_IMETHODIMP
     332           0 : nsPerformanceGroupDetails::GetProcessId(uint64_t* processId) {
     333           0 :   *processId = ProcessId();
     334           0 :   return NS_OK;
     335             : }
     336             : 
     337             : /* readonly attribute bool IsContentProcess; */
     338             : NS_IMETHODIMP
     339           0 : nsPerformanceGroupDetails::GetIsContentProcess(bool *_retval) {
     340           0 :   *_retval = IsContentProcess();
     341           0 :   return NS_OK;
     342             : }
     343             : 
     344             : 
     345             : /* ------------------------------------------------------
     346             :  *
     347             :  * class nsPerformanceStats
     348             :  *
     349             :  */
     350             : 
     351             : class nsPerformanceStats final: public nsIPerformanceStats
     352             : {
     353             : public:
     354             :   NS_DECL_ISUPPORTS
     355             :   NS_DECL_NSIPERFORMANCESTATS
     356           0 :   NS_FORWARD_NSIPERFORMANCEGROUPDETAILS(mDetails->)
     357             : 
     358           0 :   nsPerformanceStats(nsPerformanceGroupDetails* item,
     359             :                      const PerformanceData& aPerformanceData)
     360           0 :     : mDetails(item)
     361           0 :     , mPerformanceData(aPerformanceData)
     362             :   {
     363           0 :   }
     364             : 
     365             : 
     366             : private:
     367             :   RefPtr<nsPerformanceGroupDetails> mDetails;
     368             :   PerformanceData mPerformanceData;
     369             : 
     370           0 :   ~nsPerformanceStats() {}
     371             : };
     372             : 
     373           0 : NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats, nsIPerformanceGroupDetails)
     374             : 
     375             : /* readonly attribute unsigned long long totalUserTime; */
     376             : NS_IMETHODIMP
     377           0 : nsPerformanceStats::GetTotalUserTime(uint64_t *aTotalUserTime) {
     378           0 :   *aTotalUserTime = mPerformanceData.mTotalUserTime;
     379           0 :   return NS_OK;
     380             : };
     381             : 
     382             : /* readonly attribute unsigned long long totalSystemTime; */
     383             : NS_IMETHODIMP
     384           0 : nsPerformanceStats::GetTotalSystemTime(uint64_t *aTotalSystemTime) {
     385           0 :   *aTotalSystemTime = mPerformanceData.mTotalSystemTime;
     386           0 :   return NS_OK;
     387             : };
     388             : 
     389             : /* readonly attribute unsigned long long totalCPOWTime; */
     390             : NS_IMETHODIMP
     391           0 : nsPerformanceStats::GetTotalCPOWTime(uint64_t *aCpowTime) {
     392           0 :   *aCpowTime = mPerformanceData.mTotalCPOWTime;
     393           0 :   return NS_OK;
     394             : };
     395             : 
     396             : /* readonly attribute unsigned long long ticks; */
     397             : NS_IMETHODIMP
     398           0 : nsPerformanceStats::GetTicks(uint64_t *aTicks) {
     399           0 :   *aTicks = mPerformanceData.mTicks;
     400           0 :   return NS_OK;
     401             : };
     402             : 
     403             : /* void getDurations (out unsigned long aCount, [array, size_is (aCount), retval] out unsigned long long aNumberOfOccurrences); */
     404             : NS_IMETHODIMP
     405           0 : nsPerformanceStats::GetDurations(uint32_t *aCount, uint64_t **aNumberOfOccurrences) {
     406           0 :   const size_t length = mozilla::ArrayLength(mPerformanceData.mDurations);
     407           0 :   if (aCount) {
     408           0 :     *aCount = length;
     409             :   }
     410           0 :   *aNumberOfOccurrences = new uint64_t[length];
     411           0 :   for (size_t i = 0; i < length; ++i) {
     412           0 :     (*aNumberOfOccurrences)[i] = mPerformanceData.mDurations[i];
     413             :   }
     414           0 :   return NS_OK;
     415             : };
     416             : 
     417             : 
     418             : /* ------------------------------------------------------
     419             :  *
     420             :  * struct nsPerformanceSnapshot
     421             :  *
     422             :  */
     423             : 
     424             : class nsPerformanceSnapshot final : public nsIPerformanceSnapshot
     425             : {
     426             : public:
     427             :   NS_DECL_ISUPPORTS
     428             :   NS_DECL_NSIPERFORMANCESNAPSHOT
     429             : 
     430           0 :   nsPerformanceSnapshot() {}
     431             : 
     432             :   /**
     433             :    * Append statistics to the list of components data.
     434             :    */
     435             :   void AppendComponentsStats(nsIPerformanceStats* stats);
     436             : 
     437             :   /**
     438             :    * Set the statistics attached to process data.
     439             :    */
     440             :   void SetProcessStats(nsIPerformanceStats* group);
     441             : 
     442             : private:
     443           0 :   ~nsPerformanceSnapshot() {}
     444             : 
     445             : private:
     446             :   /**
     447             :    * The data for all components.
     448             :    */
     449             :   nsCOMArray<nsIPerformanceStats> mComponentsData;
     450             : 
     451             :   /**
     452             :    * The data for the process.
     453             :    */
     454             :   nsCOMPtr<nsIPerformanceStats> mProcessData;
     455             : };
     456             : 
     457           0 : NS_IMPL_ISUPPORTS(nsPerformanceSnapshot, nsIPerformanceSnapshot)
     458             : 
     459             : 
     460             : /* nsIArray getComponentsData (); */
     461             : NS_IMETHODIMP
     462           0 : nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents)
     463             : {
     464           0 :   const size_t length = mComponentsData.Length();
     465           0 :   nsCOMPtr<nsIMutableArray> components = do_CreateInstance(NS_ARRAY_CONTRACTID);
     466           0 :   for (size_t i = 0; i < length; ++i) {
     467           0 :     nsCOMPtr<nsIPerformanceStats> stats = mComponentsData[i];
     468           0 :     mozilla::DebugOnly<nsresult> rv = components->AppendElement(stats, false);
     469           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     470             :   }
     471           0 :   components.forget(aComponents);
     472           0 :   return NS_OK;
     473             : }
     474             : 
     475             : /* nsIPerformanceStats getProcessData (); */
     476             : NS_IMETHODIMP
     477           0 : nsPerformanceSnapshot::GetProcessData(nsIPerformanceStats * *aProcess)
     478             : {
     479           0 :   NS_IF_ADDREF(*aProcess = mProcessData);
     480           0 :   return NS_OK;
     481             : }
     482             : 
     483             : void
     484           0 : nsPerformanceSnapshot::AppendComponentsStats(nsIPerformanceStats* stats)
     485             : {
     486           0 :   mComponentsData.AppendElement(stats);
     487           0 : }
     488             : 
     489             : void
     490           0 : nsPerformanceSnapshot::SetProcessStats(nsIPerformanceStats* stats)
     491             : {
     492           0 :   mProcessData = stats;
     493           0 : }
     494             : 
     495             : 
     496             : 
     497             : /* ------------------------------------------------------
     498             :  *
     499             :  * class PerformanceAlert
     500             :  *
     501             :  */
     502             : class PerformanceAlert final: public nsIPerformanceAlert {
     503             : public:
     504             :   NS_DECL_ISUPPORTS
     505             :   NS_DECL_NSIPERFORMANCEALERT
     506             : 
     507             :   PerformanceAlert(const uint32_t reason, nsPerformanceGroup* source);
     508             : private:
     509           0 :   ~PerformanceAlert() {}
     510             : 
     511             :   const uint32_t mReason;
     512             : 
     513             :   // The highest values reached by this group since the latest alert,
     514             :   // in microseconds.
     515             :   const uint64_t mHighestJank;
     516             :   const uint64_t mHighestCPOW;
     517             : };
     518             : 
     519           0 : NS_IMPL_ISUPPORTS(PerformanceAlert, nsIPerformanceAlert);
     520             : 
     521           0 : PerformanceAlert::PerformanceAlert(const uint32_t reason, nsPerformanceGroup* source)
     522             :   : mReason(reason)
     523           0 :   , mHighestJank(source->HighestRecentJank())
     524           0 :   , mHighestCPOW(source->HighestRecentCPOW())
     525           0 : { }
     526             : 
     527             : NS_IMETHODIMP
     528           0 : PerformanceAlert::GetHighestJank(uint64_t* result) {
     529           0 :   *result = mHighestJank;
     530           0 :   return NS_OK;
     531             : }
     532             : 
     533             : NS_IMETHODIMP
     534           0 : PerformanceAlert::GetHighestCPOW(uint64_t* result) {
     535           0 :   *result = mHighestCPOW;
     536           0 :   return NS_OK;
     537             : }
     538             : 
     539             : NS_IMETHODIMP
     540           0 : PerformanceAlert::GetReason(uint32_t* result) {
     541           0 :   *result = mReason;
     542           0 :   return NS_OK;
     543             : }
     544             : /* ------------------------------------------------------
     545             :  *
     546             :  * class PendingAlertsCollector
     547             :  *
     548             :  */
     549             : 
     550             : /**
     551             :  * A timer callback in charge of collecting the groups in
     552             :  * `mPendingAlerts` and triggering dispatch of performance alerts.
     553             :  */
     554             : class PendingAlertsCollector final :
     555             :   public nsITimerCallback,
     556             :   public nsINamed
     557             : {
     558             : public:
     559             :   NS_DECL_ISUPPORTS
     560             :   NS_DECL_NSITIMERCALLBACK
     561             :   NS_DECL_NSINAMED
     562             : 
     563           0 :   explicit PendingAlertsCollector(nsPerformanceStatsService* service)
     564           0 :     : mService(service)
     565           0 :     , mPending(false)
     566           0 :   { }
     567             : 
     568             :   nsresult Start(uint32_t timerDelayMS);
     569             :   nsresult Dispose();
     570             : 
     571             : private:
     572           0 :   ~PendingAlertsCollector() {}
     573             : 
     574             :   RefPtr<nsPerformanceStatsService> mService;
     575             :   bool mPending;
     576             : 
     577             :   nsCOMPtr<nsITimer> mTimer;
     578             : 
     579             :   mozilla::Vector<uint64_t> mJankLevels;
     580             : };
     581             : 
     582           0 : NS_IMPL_ISUPPORTS(PendingAlertsCollector, nsITimerCallback, nsINamed);
     583             : 
     584             : NS_IMETHODIMP
     585           0 : PendingAlertsCollector::Notify(nsITimer*) {
     586           0 :   mPending = false;
     587           0 :   mService->NotifyJankObservers(mJankLevels);
     588           0 :   return NS_OK;
     589             : }
     590             : 
     591             : NS_IMETHODIMP
     592           0 : PendingAlertsCollector::GetName(nsACString& aName)
     593             : {
     594           0 :   aName.AssignASCII("PendingAlertsCollector_timer");
     595           0 :   return NS_OK;
     596             : }
     597             : 
     598             : NS_IMETHODIMP
     599           0 : PendingAlertsCollector::SetName(const char* aName)
     600             : {
     601           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     602             : }
     603             : 
     604             : nsresult
     605           0 : PendingAlertsCollector::Start(uint32_t timerDelayMS) {
     606           0 :   if (mPending) {
     607             :     // Collector is already started.
     608           0 :     return NS_OK;
     609             :   }
     610             : 
     611           0 :   if (!mTimer) {
     612           0 :     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     613             :   }
     614             : 
     615           0 :   nsresult rv = mTimer->InitWithCallback(this, timerDelayMS, nsITimer::TYPE_ONE_SHOT);
     616           0 :   if (NS_FAILED(rv)) {
     617           0 :     return rv;
     618             :   }
     619             : 
     620           0 :   mPending = true;
     621             :   {
     622           0 :     mozilla::DebugOnly<bool> result = nsRefreshDriver::GetJankLevels(mJankLevels);
     623           0 :     MOZ_ASSERT(result);
     624             :   }
     625             : 
     626           0 :   return NS_OK;
     627             : }
     628             : 
     629             : nsresult
     630           0 : PendingAlertsCollector::Dispose() {
     631           0 :   if (mTimer) {
     632           0 :     mozilla::Unused << mTimer->Cancel();
     633           0 :     mTimer = nullptr;
     634             :   }
     635           0 :   mService = nullptr;
     636           0 :   return NS_OK;
     637             : }
     638             : 
     639             : 
     640             : 
     641             : /* ------------------------------------------------------
     642             :  *
     643             :  * class nsPerformanceStatsService
     644             :  *
     645             :  */
     646             : 
     647           0 : NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService, nsIObserver)
     648             : 
     649           0 : nsPerformanceStatsService::nsPerformanceStatsService()
     650             :   : mIsAvailable(false)
     651             :   , mDisposed(false)
     652             : #if defined(XP_WIN)
     653             :   , mProcessId(GetCurrentProcessId())
     654             : #else
     655           0 :   , mProcessId(getpid())
     656             : #endif
     657             :   , mUIdCounter(0)
     658             :   , mTopGroup(nsPerformanceGroup::Make(this,
     659           0 :                                        NS_LITERAL_STRING("<process>"), // name
     660             :                                        0,    // windowId
     661           0 :                                        mProcessId,
     662             :                                        true, // isSystem
     663             :                                        nsPerformanceGroup::GroupScope::RUNTIME // scope
     664           0 :                                      ))
     665             :   , mIsHandlingUserInput(false)
     666             :   , mProcessStayed(0)
     667             :   , mProcessMoved(0)
     668             :   , mProcessUpdateCounter(0)
     669             :   , mIsMonitoringPerCompartment(false)
     670             :   , mJankAlertThreshold(mozilla::MaxValue<uint64_t>::value) // By default, no alerts
     671             :   , mJankAlertBufferingDelay(1000 /* ms */)
     672             :   , mJankLevelVisibilityThreshold(/* 2 ^ */ 8 /* ms */)
     673           0 :   , mMaxExpectedDurationOfInteractionUS(150 * 1000)
     674             : {
     675           0 :   mPendingAlertsCollector = new PendingAlertsCollector(this);
     676             : 
     677           0 :   nsString groupIdForWindows;
     678           0 :   GenerateUniqueGroupId(GetNextId(), mProcessId, groupIdForWindows);
     679             :   mUniversalTargets.mWindows->
     680           0 :     SetTarget(new nsPerformanceGroupDetails(NS_LITERAL_STRING("<universal window listener>"),
     681             :                                             groupIdForWindows,
     682             :                                             0, // window id
     683           0 :                                             mProcessId,
     684           0 :                                             false));
     685           0 : }
     686             : 
     687           0 : nsPerformanceStatsService::~nsPerformanceStatsService()
     688           0 : { }
     689             : 
     690             : /**
     691             :  * Clean up the service.
     692             :  *
     693             :  * Called during shutdown. Idempotent.
     694             :  */
     695             : void
     696           0 : nsPerformanceStatsService::Dispose()
     697             : {
     698             :   // Make sure that we do not accidentally destroy `this` while we are
     699             :   // cleaning up back references.
     700           0 :   RefPtr<nsPerformanceStatsService> kungFuDeathGrip(this);
     701           0 :   mIsAvailable = false;
     702             : 
     703           0 :   if (mDisposed) {
     704             :     // Make sure that we don't double-dispose.
     705           0 :     return;
     706             :   }
     707           0 :   mDisposed = true;
     708             : 
     709             :   // Disconnect from nsIObserverService.
     710           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     711           0 :   if (obs) {
     712           0 :     for (size_t i = 0; i < mozilla::ArrayLength(TOPICS); ++i) {
     713           0 :       mozilla::Unused << obs->RemoveObserver(this, TOPICS[i]);
     714             :     }
     715             :   }
     716             : 
     717             :   // Clear up and disconnect from JSAPI.
     718           0 :   mozilla::dom::AutoJSAPI jsapi;
     719           0 :   jsapi.Init();
     720           0 :   JSContext* cx = jsapi.cx();
     721           0 :   js::DisposePerformanceMonitoring(cx);
     722             : 
     723           0 :   mozilla::Unused << js::SetStopwatchIsMonitoringCPOW(cx, false);
     724           0 :   mozilla::Unused << js::SetStopwatchIsMonitoringJank(cx, false);
     725             : 
     726           0 :   mozilla::Unused << js::SetStopwatchStartCallback(cx, nullptr, nullptr);
     727           0 :   mozilla::Unused << js::SetStopwatchCommitCallback(cx, nullptr, nullptr);
     728           0 :   mozilla::Unused << js::SetGetPerformanceGroupsCallback(cx, nullptr, nullptr);
     729             : 
     730             :   // Clear up and disconnect the alerts collector.
     731           0 :   if (mPendingAlertsCollector) {
     732           0 :     mPendingAlertsCollector->Dispose();
     733           0 :     mPendingAlertsCollector = nullptr;
     734             :   }
     735           0 :   mPendingAlerts.clear();
     736             : 
     737             :   // Disconnect universal observers. Per-group observers will be
     738             :   // disconnected below as part of `group->Dispose()`.
     739           0 :   mUniversalTargets.mWindows = nullptr;
     740             : 
     741             :   // At this stage, the JS VM may still be holding references to
     742             :   // instances of PerformanceGroup on the stack. To let the service be
     743             :   // collected, we need to break the references from these groups to
     744             :   // `this`.
     745           0 :   mTopGroup->Dispose();
     746           0 :   mTopGroup = nullptr;
     747             : 
     748             :   // Copy references to the groups to a vector to ensure that we do
     749             :   // not modify the hashtable while iterating it.
     750           0 :   GroupVector groups;
     751           0 :   for (auto iter = mGroups.Iter(); !iter.Done(); iter.Next()) {
     752           0 :     if (!groups.append(iter.Get()->GetKey())) {
     753           0 :       MOZ_CRASH();
     754             :     }
     755             :   }
     756           0 :   for (auto iter = groups.begin(), end = groups.end(); iter < end; ++iter) {
     757           0 :     RefPtr<nsPerformanceGroup> group = *iter;
     758           0 :     group->Dispose();
     759             :   }
     760             : 
     761             :   // Any remaining references to PerformanceGroup will be released as
     762             :   // the VM unrolls the stack. If there are any nested event loops,
     763             :   // this may take time.
     764             : }
     765             : 
     766             : nsresult
     767           0 : nsPerformanceStatsService::Init()
     768             : {
     769           0 :   nsresult rv = InitInternal();
     770           0 :   if (NS_FAILED(rv)) {
     771             :     // Attempt to clean up.
     772           0 :     Dispose();
     773             :   }
     774           0 :   return rv;
     775             : }
     776             : 
     777             : nsresult
     778           0 : nsPerformanceStatsService::InitInternal()
     779             : {
     780             :   // Make sure that we release everything during shutdown.
     781             :   // We are a bit defensive here, as we know that some strange behavior can break the
     782             :   // regular shutdown order.
     783           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     784           0 :   if (obs) {
     785           0 :     for (size_t i = 0; i < mozilla::ArrayLength(TOPICS); ++i) {
     786           0 :       mozilla::Unused << obs->AddObserver(this, TOPICS[i], false);
     787             :     }
     788             :   }
     789             : 
     790             :   // Connect to JSAPI.
     791           0 :   mozilla::dom::AutoJSAPI jsapi;
     792           0 :   jsapi.Init();
     793           0 :   JSContext* cx = jsapi.cx();
     794           0 :   if (!js::SetStopwatchStartCallback(cx, StopwatchStartCallback, this)) {
     795           0 :     return NS_ERROR_UNEXPECTED;
     796             :   }
     797           0 :   if (!js::SetStopwatchCommitCallback(cx, StopwatchCommitCallback, this)) {
     798           0 :     return NS_ERROR_UNEXPECTED;
     799             :   }
     800           0 :   if (!js::SetGetPerformanceGroupsCallback(cx, GetPerformanceGroupsCallback, this)) {
     801           0 :     return NS_ERROR_UNEXPECTED;
     802             :   }
     803             : 
     804           0 :   mTopGroup->setIsActive(true);
     805           0 :   mIsAvailable = true;
     806             : 
     807           0 :   return NS_OK;
     808             : }
     809             : 
     810             : // Observe shutdown events.
     811             : NS_IMETHODIMP
     812           0 : nsPerformanceStatsService::Observe(nsISupports *aSubject, const char *aTopic,
     813             :                                    const char16_t *aData)
     814             : {
     815           0 :   MOZ_ASSERT(strcmp(aTopic, "profile-before-change") == 0
     816             :              || strcmp(aTopic, "quit-application") == 0
     817             :              || strcmp(aTopic, "quit-application-granted") == 0
     818             :              || strcmp(aTopic, "xpcom-will-shutdown") == 0);
     819             : 
     820           0 :   Dispose();
     821           0 :   return NS_OK;
     822             : }
     823             : 
     824             : /*static*/ bool
     825           0 : nsPerformanceStatsService::IsHandlingUserInput() {
     826           0 :   if (mozilla::EventStateManager::LatestUserInputStart().IsNull()) {
     827           0 :     return false;
     828             :   }
     829           0 :   bool result = mozilla::TimeStamp::Now() - mozilla::EventStateManager::LatestUserInputStart() <= mozilla::TimeDuration::FromMicroseconds(mMaxExpectedDurationOfInteractionUS);
     830           0 :   return result;
     831             : }
     832             : 
     833             : /* [implicit_jscontext] attribute bool isMonitoringCPOW; */
     834             : NS_IMETHODIMP
     835           0 : nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive)
     836             : {
     837           0 :   if (!mIsAvailable) {
     838           0 :     return NS_ERROR_NOT_AVAILABLE;
     839             :   }
     840             : 
     841           0 :   *aIsStopwatchActive = js::GetStopwatchIsMonitoringCPOW(cx);
     842           0 :   return NS_OK;
     843             : }
     844             : NS_IMETHODIMP
     845           0 : nsPerformanceStatsService::SetIsMonitoringCPOW(JSContext* cx, bool aIsStopwatchActive)
     846             : {
     847           0 :   if (!mIsAvailable) {
     848           0 :     return NS_ERROR_NOT_AVAILABLE;
     849             :   }
     850             : 
     851           0 :   if (!js::SetStopwatchIsMonitoringCPOW(cx, aIsStopwatchActive)) {
     852           0 :     return NS_ERROR_OUT_OF_MEMORY;
     853             :   }
     854           0 :   return NS_OK;
     855             : }
     856             : 
     857             : /* [implicit_jscontext] attribute bool isMonitoringJank; */
     858             : NS_IMETHODIMP
     859           0 : nsPerformanceStatsService::GetIsMonitoringJank(JSContext* cx, bool *aIsStopwatchActive)
     860             : {
     861           0 :   if (!mIsAvailable) {
     862           0 :     return NS_ERROR_NOT_AVAILABLE;
     863             :   }
     864             : 
     865           0 :   *aIsStopwatchActive = js::GetStopwatchIsMonitoringJank(cx);
     866           0 :   return NS_OK;
     867             : }
     868             : NS_IMETHODIMP
     869           0 : nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool aIsStopwatchActive)
     870             : {
     871           0 :   if (!mIsAvailable) {
     872           0 :     return NS_ERROR_NOT_AVAILABLE;
     873             :   }
     874             : 
     875           0 :   if (!js::SetStopwatchIsMonitoringJank(cx, aIsStopwatchActive)) {
     876           0 :     return NS_ERROR_OUT_OF_MEMORY;
     877             :   }
     878           0 :   return NS_OK;
     879             : }
     880             : 
     881             : /* [implicit_jscontext] attribute bool isMonitoringPerCompartment; */
     882             : NS_IMETHODIMP
     883           0 : nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext*, bool *aIsMonitoringPerCompartment)
     884             : {
     885           0 :   if (!mIsAvailable) {
     886           0 :     return NS_ERROR_NOT_AVAILABLE;
     887             :   }
     888             : 
     889           0 :   *aIsMonitoringPerCompartment = mIsMonitoringPerCompartment;
     890           0 :   return NS_OK;
     891             : }
     892             : NS_IMETHODIMP
     893           0 : nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext*, bool aIsMonitoringPerCompartment)
     894             : {
     895           0 :   if (!mIsAvailable) {
     896           0 :     return NS_ERROR_NOT_AVAILABLE;
     897             :   }
     898             : 
     899           0 :   if (aIsMonitoringPerCompartment == mIsMonitoringPerCompartment) {
     900           0 :     return NS_OK;
     901             :   }
     902             : 
     903             :   // Relatively slow update: walk the entire lost of performance groups,
     904             :   // update the active flag of those that have changed.
     905             :   //
     906             :   // Alternative strategies could be envisioned to make the update
     907             :   // much faster, at the expense of the speed of calling `isActive()`,
     908             :   // (e.g. deferring `isActive()` to the nsPerformanceStatsService),
     909             :   // but we expect that `isActive()` can be called thousands of times
     910             :   // per second, while `SetIsMonitoringPerCompartment` is not called
     911             :   // at all during most Firefox runs.
     912             : 
     913           0 :   for (auto iter = mGroups.Iter(); !iter.Done(); iter.Next()) {
     914           0 :     RefPtr<nsPerformanceGroup> group = iter.Get()->GetKey();
     915           0 :     if (group->Scope() == nsPerformanceGroup::GroupScope::COMPARTMENT) {
     916           0 :       group->setIsActive(aIsMonitoringPerCompartment);
     917             :     }
     918             :   }
     919           0 :   mIsMonitoringPerCompartment = aIsMonitoringPerCompartment;
     920           0 :   return NS_OK;
     921             : }
     922             : 
     923             : NS_IMETHODIMP
     924           0 : nsPerformanceStatsService::GetJankAlertThreshold(uint64_t* result) {
     925           0 :   *result = mJankAlertThreshold;
     926           0 :   return NS_OK;
     927             : }
     928             : 
     929             : NS_IMETHODIMP
     930           0 : nsPerformanceStatsService::SetJankAlertThreshold(uint64_t value) {
     931           0 :   mJankAlertThreshold = value;
     932           0 :   return NS_OK;
     933             : }
     934             : 
     935             : NS_IMETHODIMP
     936           0 : nsPerformanceStatsService::GetJankAlertBufferingDelay(uint32_t* result) {
     937           0 :   *result = mJankAlertBufferingDelay;
     938           0 :   return NS_OK;
     939             : }
     940             : 
     941             : NS_IMETHODIMP
     942           0 : nsPerformanceStatsService::SetJankAlertBufferingDelay(uint32_t value) {
     943           0 :   mJankAlertBufferingDelay = value;
     944           0 :   return NS_OK;
     945             : }
     946             : 
     947             : nsresult
     948           0 : nsPerformanceStatsService::UpdateTelemetry()
     949             : {
     950             :   // Promote everything to floating-point explicitly before dividing.
     951           0 :   const double processStayed = mProcessStayed;
     952           0 :   const double processMoved = mProcessMoved;
     953             : 
     954           0 :   if (processStayed <= 0 || processMoved <= 0 || processStayed + processMoved <= 0) {
     955             :     // Overflow/underflow/nothing to report
     956           0 :     return NS_OK;
     957             :   }
     958             : 
     959           0 :   const double proportion = (100 * processStayed) / (processStayed + processMoved);
     960           0 :   if (proportion < 0 || proportion > 100) {
     961             :     // Overflow/underflow
     962           0 :     return NS_OK;
     963             :   }
     964             : 
     965           0 :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED, (uint32_t)proportion);
     966           0 :   return NS_OK;
     967             : }
     968             : 
     969             : 
     970             : /* static */ nsIPerformanceStats*
     971           0 : nsPerformanceStatsService::GetStatsForGroup(const js::PerformanceGroup* group)
     972             : {
     973           0 :   return GetStatsForGroup(nsPerformanceGroup::Get(group));
     974             : }
     975             : 
     976             : /* static */ nsIPerformanceStats*
     977           0 : nsPerformanceStatsService::GetStatsForGroup(const nsPerformanceGroup* group)
     978             : {
     979           0 :   return new nsPerformanceStats(group->Details(), group->data);
     980             : }
     981             : 
     982             : /* [implicit_jscontext] nsIPerformanceSnapshot getSnapshot (); */
     983             : NS_IMETHODIMP
     984           0 : nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
     985             : {
     986           0 :   if (!mIsAvailable) {
     987           0 :     return NS_ERROR_NOT_AVAILABLE;
     988             :   }
     989             : 
     990           0 :   RefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
     991           0 :   snapshot->SetProcessStats(GetStatsForGroup(mTopGroup));
     992             : 
     993           0 :   for (auto iter = mGroups.Iter(); !iter.Done(); iter.Next()) {
     994           0 :     auto* entry = iter.Get();
     995           0 :     nsPerformanceGroup* group = entry->GetKey();
     996           0 :     if (group->isActive()) {
     997           0 :       snapshot->AppendComponentsStats(GetStatsForGroup(group));
     998             :     }
     999             :   }
    1000             : 
    1001           0 :   js::GetPerfMonitoringTestCpuRescheduling(cx, &mProcessStayed, &mProcessMoved);
    1002             : 
    1003           0 :   if (++mProcessUpdateCounter % 10 == 0) {
    1004           0 :     mozilla::Unused << UpdateTelemetry();
    1005             :   }
    1006             : 
    1007           0 :   snapshot.forget(aSnapshot);
    1008             : 
    1009           0 :   return NS_OK;
    1010             : }
    1011             : 
    1012             : uint64_t
    1013           0 : nsPerformanceStatsService::GetNextId() {
    1014           0 :   return ++mUIdCounter;
    1015             : }
    1016             : 
    1017             : /* static*/ bool
    1018           0 : nsPerformanceStatsService::GetPerformanceGroupsCallback(JSContext* cx,
    1019             :                                                         js::PerformanceGroupVector& out,
    1020             :                                                         void* closure)
    1021             : {
    1022           0 :   RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
    1023           0 :   return self->GetPerformanceGroups(cx, out);
    1024             : }
    1025             : 
    1026             : bool
    1027           0 : nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx,
    1028             :                                                 js::PerformanceGroupVector& out)
    1029             : {
    1030           0 :   JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
    1031           0 :   if (!global) {
    1032             :     // While it is possible for a compartment to have no global
    1033             :     // (e.g. atoms), this compartment is not very interesting for us.
    1034           0 :     return true;
    1035             :   }
    1036             : 
    1037             :   // All compartments belong to the top group.
    1038           0 :   if (!out.append(mTopGroup)) {
    1039           0 :     JS_ReportOutOfMemory(cx);
    1040           0 :     return false;
    1041             :   }
    1042             : 
    1043           0 :   nsAutoString name;
    1044           0 :   CompartmentName(cx, global, name);
    1045           0 :   bool isSystem = nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
    1046             : 
    1047             :   // Find out if the compartment is executed by a window. If so, its
    1048             :   // duration should count towards the total duration of the window.
    1049           0 :   uint64_t windowId = 0;
    1050           0 :   if (nsCOMPtr<nsPIDOMWindowOuter> ptop = GetPrivateWindow(cx)) {
    1051           0 :     windowId = ptop->WindowID();
    1052           0 :     auto entry = mWindowIdToGroup.PutEntry(windowId);
    1053           0 :     if (!entry->GetGroup()) {
    1054           0 :       nsString windowName = name;
    1055           0 :       windowName.AppendLiteral(" (as window ");
    1056           0 :       windowName.AppendInt(windowId);
    1057           0 :       windowName.AppendLiteral(")");
    1058             :       entry->
    1059           0 :         SetGroup(nsPerformanceGroup::Make(this,
    1060             :                                           windowName, windowId,
    1061           0 :                                           mProcessId, isSystem,
    1062             :                                           nsPerformanceGroup::GroupScope::WINDOW)
    1063           0 :                  );
    1064             :     }
    1065           0 :     if (!out.append(entry->GetGroup())) {
    1066           0 :       JS_ReportOutOfMemory(cx);
    1067           0 :       return false;
    1068             :     }
    1069             :   }
    1070             : 
    1071             :   // All compartments have their own group.
    1072             :   auto group =
    1073           0 :     nsPerformanceGroup::Make(this,
    1074             :                              name, windowId,
    1075           0 :                              mProcessId, isSystem,
    1076           0 :                              nsPerformanceGroup::GroupScope::COMPARTMENT);
    1077           0 :   if (!out.append(group)) {
    1078           0 :     JS_ReportOutOfMemory(cx);
    1079           0 :     return false;
    1080             :   }
    1081             : 
    1082             :   // Returning a vector that is too large would cause allocations all over the
    1083             :   // place in the JS engine. We want to be sure that all data is stored inline.
    1084           0 :   MOZ_ASSERT(out.length() <= out.sMaxInlineStorage);
    1085           0 :   return true;
    1086             : }
    1087             : 
    1088             : /*static*/ bool
    1089           0 : nsPerformanceStatsService::StopwatchStartCallback(uint64_t iteration, void* closure) {
    1090           0 :   RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
    1091           0 :   return self->StopwatchStart(iteration);
    1092             : }
    1093             : 
    1094             : bool
    1095           0 : nsPerformanceStatsService::StopwatchStart(uint64_t iteration) {
    1096           0 :   mIteration = iteration;
    1097             : 
    1098           0 :   mIsHandlingUserInput = IsHandlingUserInput();
    1099           0 :   mUserInputCount = mozilla::EventStateManager::UserInputCount();
    1100             : 
    1101           0 :   nsresult rv = GetResources(&mUserTimeStart, &mSystemTimeStart);
    1102           0 :   if (NS_FAILED(rv)) {
    1103           0 :     return false;
    1104             :   }
    1105             : 
    1106           0 :   return true;
    1107             : }
    1108             : 
    1109             : /*static*/ bool
    1110           0 : nsPerformanceStatsService::StopwatchCommitCallback(uint64_t iteration,
    1111             :                                                    js::PerformanceGroupVector& recentGroups,
    1112             :                                                    void* closure)
    1113             : {
    1114           0 :   RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
    1115           0 :   return self->StopwatchCommit(iteration, recentGroups);
    1116             : }
    1117             : 
    1118             : bool
    1119           0 : nsPerformanceStatsService::StopwatchCommit(uint64_t iteration,
    1120             :                                            js::PerformanceGroupVector& recentGroups)
    1121             : {
    1122           0 :   MOZ_ASSERT(iteration == mIteration);
    1123           0 :   MOZ_ASSERT(!recentGroups.empty());
    1124             : 
    1125             :   uint64_t userTimeStop, systemTimeStop;
    1126           0 :   nsresult rv = GetResources(&userTimeStop, &systemTimeStop);
    1127           0 :   if (NS_FAILED(rv)) {
    1128           0 :     return false;
    1129             :   }
    1130             : 
    1131             :   // `GetResources` is not guaranteed to be monotonic, so round up
    1132             :   // any negative result to 0 milliseconds.
    1133           0 :   uint64_t userTimeDelta = 0;
    1134           0 :   if (userTimeStop > mUserTimeStart)
    1135           0 :     userTimeDelta = userTimeStop - mUserTimeStart;
    1136             : 
    1137           0 :   uint64_t systemTimeDelta = 0;
    1138           0 :   if (systemTimeStop > mSystemTimeStart)
    1139           0 :     systemTimeDelta = systemTimeStop - mSystemTimeStart;
    1140             : 
    1141           0 :   MOZ_ASSERT(mTopGroup->isUsedInThisIteration());
    1142           0 :   const uint64_t totalRecentCycles = mTopGroup->recentCycles(iteration);
    1143             : 
    1144           0 :   const bool isHandlingUserInput = mIsHandlingUserInput || mozilla::EventStateManager::UserInputCount() > mUserInputCount;
    1145             : 
    1146             :   // We should only reach this stage if `group` has had some activity.
    1147           0 :   MOZ_ASSERT(mTopGroup->recentTicks(iteration) > 0);
    1148           0 :   for (auto iter = recentGroups.begin(), end = recentGroups.end(); iter != end; ++iter) {
    1149           0 :     RefPtr<nsPerformanceGroup> group = nsPerformanceGroup::Get(*iter);
    1150           0 :     CommitGroup(iteration, userTimeDelta, systemTimeDelta, totalRecentCycles, isHandlingUserInput, group);
    1151             :   }
    1152             : 
    1153             :   // Make sure that `group` was treated along with the other items of `recentGroups`.
    1154           0 :   MOZ_ASSERT(!mTopGroup->isUsedInThisIteration());
    1155           0 :   MOZ_ASSERT(mTopGroup->recentTicks(iteration) == 0);
    1156             : 
    1157           0 :   if (!mPendingAlerts.empty()) {
    1158           0 :     mPendingAlertsCollector->Start(mJankAlertBufferingDelay);
    1159             :   }
    1160             : 
    1161           0 :   return true;
    1162             : }
    1163             : 
    1164             : void
    1165           0 : nsPerformanceStatsService::CommitGroup(uint64_t iteration,
    1166             :                                        uint64_t totalUserTimeDelta, uint64_t totalSystemTimeDelta,
    1167             :                                        uint64_t totalCyclesDelta,
    1168             :                                        bool isHandlingUserInput,
    1169             :                                        nsPerformanceGroup* group) {
    1170             : 
    1171           0 :   MOZ_ASSERT(group->isUsedInThisIteration());
    1172             : 
    1173           0 :   const uint64_t ticksDelta = group->recentTicks(iteration);
    1174           0 :   const uint64_t cpowTimeDelta = group->recentCPOW(iteration);
    1175           0 :   const uint64_t cyclesDelta = group->recentCycles(iteration);
    1176           0 :   group->resetRecentData();
    1177             : 
    1178             :   // We have now performed all cleanup and may `return` at any time without fear of leaks.
    1179             : 
    1180           0 :   if (group->iteration() != iteration) {
    1181             :     // Stale data, don't commit.
    1182           0 :     return;
    1183             :   }
    1184             : 
    1185             :   // When we add a group as changed, we immediately set its
    1186             :   // `recentTicks` from 0 to 1.  If we have `ticksDelta == 0` at
    1187             :   // this stage, we have already called `resetRecentData` but we
    1188             :   // haven't removed it from the list.
    1189           0 :   MOZ_ASSERT(ticksDelta != 0);
    1190           0 :   MOZ_ASSERT(cyclesDelta <= totalCyclesDelta);
    1191           0 :   if (cyclesDelta == 0 || totalCyclesDelta == 0) {
    1192             :     // Nothing useful, don't commit.
    1193           0 :     return;
    1194             :   }
    1195             : 
    1196           0 :   double proportion = (double)cyclesDelta / (double)totalCyclesDelta;
    1197           0 :   MOZ_ASSERT(proportion <= 1);
    1198             : 
    1199           0 :   const uint64_t userTimeDelta = proportion * totalUserTimeDelta;
    1200           0 :   const uint64_t systemTimeDelta = proportion * totalSystemTimeDelta;
    1201             : 
    1202           0 :   group->data.mTotalUserTime += userTimeDelta;
    1203           0 :   group->data.mTotalSystemTime += systemTimeDelta;
    1204           0 :   group->data.mTotalCPOWTime += cpowTimeDelta;
    1205           0 :   group->data.mTicks += ticksDelta;
    1206             : 
    1207           0 :   const uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta + cpowTimeDelta;
    1208           0 :   uint64_t duration = 1000;   // 1ms in µs
    1209           0 :   for (size_t i = 0;
    1210           0 :        i < mozilla::ArrayLength(group->data.mDurations) && duration < totalTimeDelta;
    1211           0 :        ++i, duration *= 2) {
    1212           0 :     group->data.mDurations[i]++;
    1213             :   }
    1214             : 
    1215           0 :   group->RecordJank(totalTimeDelta);
    1216           0 :   group->RecordCPOW(cpowTimeDelta);
    1217           0 :   if (isHandlingUserInput) {
    1218           0 :     group->RecordUserInput();
    1219             :   }
    1220             : 
    1221           0 :   if (totalTimeDelta >= mJankAlertThreshold) {
    1222           0 :     if (!group->HasPendingAlert()) {
    1223           0 :       if (mPendingAlerts.append(group)) {
    1224           0 :         group->SetHasPendingAlert(true);
    1225             :       }
    1226           0 :       return;
    1227             :     }
    1228             :   }
    1229             : 
    1230           0 :   return;
    1231             : }
    1232             : 
    1233             : nsresult
    1234           0 : nsPerformanceStatsService::GetResources(uint64_t* userTime,
    1235             :                                         uint64_t* systemTime) const {
    1236           0 :   MOZ_ASSERT(userTime);
    1237           0 :   MOZ_ASSERT(systemTime);
    1238             : 
    1239             : #if defined(XP_MACOSX)
    1240             :   // On MacOS X, to get we per-thread data, we need to
    1241             :   // reach into the kernel.
    1242             : 
    1243             :   mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
    1244             :   thread_basic_info_data_t info;
    1245             :   mach_port_t port = mach_thread_self();
    1246             :   kern_return_t err =
    1247             :     thread_info(/* [in] targeted thread*/ port,
    1248             :                 /* [in] nature of information*/ THREAD_BASIC_INFO,
    1249             :                 /* [out] thread information */  (thread_info_t)&info,
    1250             :                 /* [inout] number of items */   &count);
    1251             : 
    1252             :   // We do not need ability to communicate with the thread, so
    1253             :   // let's release the port.
    1254             :   mach_port_deallocate(mach_task_self(), port);
    1255             : 
    1256             :   if (err != KERN_SUCCESS)
    1257             :     return NS_ERROR_FAILURE;
    1258             : 
    1259             :   *userTime = info.user_time.microseconds + info.user_time.seconds * 1000000;
    1260             :   *systemTime = info.system_time.microseconds + info.system_time.seconds * 1000000;
    1261             : 
    1262             : #elif defined(XP_UNIX)
    1263             :   struct rusage rusage;
    1264             : #if defined(RUSAGE_THREAD)
    1265             :   // Under Linux, we can obtain per-thread statistics
    1266           0 :   int err = getrusage(RUSAGE_THREAD, &rusage);
    1267             : #else
    1268             :   // Under other Unices, we need to do with more noisy
    1269             :   // per-process statistics.
    1270             :   int err = getrusage(RUSAGE_SELF, &rusage);
    1271             : #endif // defined(RUSAGE_THREAD)
    1272             : 
    1273           0 :   if (err)
    1274           0 :     return NS_ERROR_FAILURE;
    1275             : 
    1276           0 :   *userTime = rusage.ru_utime.tv_usec + rusage.ru_utime.tv_sec * 1000000;
    1277           0 :   *systemTime = rusage.ru_stime.tv_usec + rusage.ru_stime.tv_sec * 1000000;
    1278             : 
    1279             : #elif defined(XP_WIN)
    1280             :   // Under Windows, we can obtain per-thread statistics. Experience
    1281             :   // seems to suggest that they are not very accurate under Windows
    1282             :   // XP, though.
    1283             :   FILETIME creationFileTime; // Ignored
    1284             :   FILETIME exitFileTime; // Ignored
    1285             :   FILETIME kernelFileTime;
    1286             :   FILETIME userFileTime;
    1287             :   BOOL success = GetThreadTimes(GetCurrentThread(),
    1288             :                                 &creationFileTime, &exitFileTime,
    1289             :                                 &kernelFileTime, &userFileTime);
    1290             : 
    1291             :   if (!success)
    1292             :     return NS_ERROR_FAILURE;
    1293             : 
    1294             :   ULARGE_INTEGER kernelTimeInt;
    1295             :   kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
    1296             :   kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
    1297             :   // Convert 100 ns to 1 us.
    1298             :   *systemTime = kernelTimeInt.QuadPart / 10;
    1299             : 
    1300             :   ULARGE_INTEGER userTimeInt;
    1301             :   userTimeInt.LowPart = userFileTime.dwLowDateTime;
    1302             :   userTimeInt.HighPart = userFileTime.dwHighDateTime;
    1303             :   // Convert 100 ns to 1 us.
    1304             :   *userTime = userTimeInt.QuadPart / 10;
    1305             : 
    1306             : #endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
    1307             : 
    1308           0 :   return NS_OK;
    1309             : }
    1310             : 
    1311             : void
    1312           0 : nsPerformanceStatsService::NotifyJankObservers(const mozilla::Vector<uint64_t>& aPreviousJankLevels) {
    1313             : 
    1314             :   // The move operation is generally constant time, unless
    1315             :   // `mPendingAlerts.length()` is very small, in which case it's fast anyway.
    1316           0 :   GroupVector alerts(Move(mPendingAlerts));
    1317           0 :   mPendingAlerts = GroupVector(); // Reconstruct after `Move`.
    1318             : 
    1319           0 :   if (!mPendingAlertsCollector) {
    1320             :     // We are shutting down.
    1321           0 :     return;
    1322             :   }
    1323             : 
    1324             :   // Find out if we have noticed any user-noticeable delay in an
    1325             :   // animation recently (i.e. since the start of the execution of JS
    1326             :   // code that caused this collector to start). If so, we'll mark any
    1327             :   // alert as part of a user-noticeable jank. Note that this doesn't
    1328             :   // mean with any certainty that the alert is the only cause of jank,
    1329             :   // or even the main cause of jank.
    1330           0 :   mozilla::Vector<uint64_t> latestJankLevels;
    1331             :   {
    1332           0 :     mozilla::DebugOnly<bool> result = nsRefreshDriver::GetJankLevels(latestJankLevels);
    1333           0 :     MOZ_ASSERT(result);
    1334             :   }
    1335           0 :   MOZ_ASSERT(latestJankLevels.length() == aPreviousJankLevels.length());
    1336             : 
    1337           0 :   bool isJankInAnimation = false;
    1338           0 :   for (size_t i = mJankLevelVisibilityThreshold; i < latestJankLevels.length(); ++i) {
    1339           0 :     if (latestJankLevels[i] > aPreviousJankLevels[i]) {
    1340           0 :       isJankInAnimation = true;
    1341           0 :       break;
    1342             :     }
    1343             :   }
    1344             : 
    1345           0 :   MOZ_ASSERT(!alerts.empty());
    1346           0 :   const bool hasUniversalWindowObservers = mUniversalTargets.mWindows->HasObservers();
    1347           0 :   for (auto iter = alerts.begin(); iter < alerts.end(); ++iter) {
    1348           0 :     MOZ_ASSERT(iter);
    1349           0 :     RefPtr<nsPerformanceGroup> group = *iter;
    1350           0 :     group->SetHasPendingAlert(false);
    1351             : 
    1352           0 :     RefPtr<nsPerformanceGroupDetails> details = group->Details();
    1353             :     nsPerformanceObservationTarget* targets[3] = {
    1354           0 :       hasUniversalWindowObservers && details->IsWindow() ? mUniversalTargets.mWindows.get() : nullptr,
    1355           0 :       group->ObservationTarget()
    1356           0 :     };
    1357             : 
    1358           0 :     bool isJankInInput = group->HasRecentUserInput();
    1359             : 
    1360           0 :     RefPtr<PerformanceAlert> alert;
    1361           0 :     for (nsPerformanceObservationTarget* target : targets) {
    1362           0 :       if (!target) {
    1363           0 :         continue;
    1364             :       }
    1365           0 :       if (!alert) {
    1366             :         const uint32_t reason = nsIPerformanceAlert::REASON_SLOWDOWN
    1367           0 :           | (isJankInAnimation ? nsIPerformanceAlert::REASON_JANK_IN_ANIMATION : 0)
    1368           0 :           | (isJankInInput ? nsIPerformanceAlert::REASON_JANK_IN_INPUT : 0);
    1369             :         // Wait until we are sure we need to allocate before we allocate.
    1370           0 :         alert = new PerformanceAlert(reason, group);
    1371             :       }
    1372           0 :       target->NotifyJankObservers(details, alert);
    1373             :     }
    1374             : 
    1375           0 :     group->ResetRecent();
    1376             :   }
    1377             : 
    1378             : }
    1379             : 
    1380             : NS_IMETHODIMP
    1381           0 : nsPerformanceStatsService::GetObservableWindow(uint64_t windowId,
    1382             :                                                nsIPerformanceObservable** result) {
    1383           0 :   if (windowId == 0) {
    1384           0 :     NS_IF_ADDREF(*result = mUniversalTargets.mWindows);
    1385             :   } else {
    1386           0 :     auto entry = mWindowIdToGroup.PutEntry(windowId);
    1387           0 :     NS_IF_ADDREF(*result = entry->ObservationTarget());
    1388             :   }
    1389           0 :   return NS_OK;
    1390             : }
    1391             : 
    1392             : NS_IMETHODIMP
    1393           0 : nsPerformanceStatsService::GetAnimationJankLevelThreshold(short* result) {
    1394           0 :   *result = mJankLevelVisibilityThreshold;
    1395           0 :   return NS_OK;
    1396             : }
    1397             : 
    1398             : NS_IMETHODIMP
    1399           0 : nsPerformanceStatsService::SetAnimationJankLevelThreshold(short value) {
    1400           0 :   mJankLevelVisibilityThreshold = value;
    1401           0 :   return NS_OK;
    1402             : }
    1403             : 
    1404             : NS_IMETHODIMP
    1405           0 : nsPerformanceStatsService::GetUserInputDelayThreshold(uint64_t* result) {
    1406           0 :   *result = mMaxExpectedDurationOfInteractionUS;
    1407           0 :   return NS_OK;
    1408             : }
    1409             : 
    1410             : NS_IMETHODIMP
    1411           0 : nsPerformanceStatsService::SetUserInputDelayThreshold(uint64_t value) {
    1412           0 :   mMaxExpectedDurationOfInteractionUS = value;
    1413           0 :   return NS_OK;
    1414             : }
    1415             : 
    1416             : 
    1417             : 
    1418           0 : nsPerformanceStatsService::UniversalTargets::UniversalTargets()
    1419           0 :   : mWindows(new nsPerformanceObservationTarget())
    1420           0 : { }
    1421             : 
    1422             : /* ------------------------------------------------------
    1423             :  *
    1424             :  * Class nsPerformanceGroup
    1425             :  *
    1426             :  */
    1427             : 
    1428             : /*static*/ nsPerformanceGroup*
    1429           0 : nsPerformanceGroup::Make(nsPerformanceStatsService* service,
    1430             :                          const nsAString& name,
    1431             :                          uint64_t windowId,
    1432             :                          uint64_t processId,
    1433             :                          bool isSystem,
    1434             :                          GroupScope scope)
    1435             : {
    1436           0 :   nsString groupId;
    1437           0 :   ::GenerateUniqueGroupId(service->GetNextId(), processId, groupId);
    1438           0 :   return new nsPerformanceGroup(service, name, groupId, windowId, processId, isSystem, scope);
    1439             : }
    1440             : 
    1441           0 : nsPerformanceGroup::nsPerformanceGroup(nsPerformanceStatsService* service,
    1442             :                                        const nsAString& name,
    1443             :                                        const nsAString& groupId,
    1444             :                                        uint64_t windowId,
    1445             :                                        uint64_t processId,
    1446             :                                        bool isSystem,
    1447           0 :                                        GroupScope scope)
    1448           0 :   : mDetails(new nsPerformanceGroupDetails(name, groupId, windowId, processId, isSystem))
    1449             :   , mService(service)
    1450             :   , mScope(scope)
    1451             :   , mHighestJank(0)
    1452             :   , mHighestCPOW(0)
    1453             :   , mHasRecentUserInput(false)
    1454           0 :   , mHasPendingAlert(false)
    1455             : {
    1456           0 :   mozilla::Unused << mService->mGroups.PutEntry(this);
    1457             : 
    1458             : #if defined(DEBUG)
    1459           0 :   if (scope == GroupScope::WINDOW) {
    1460           0 :     MOZ_ASSERT(mDetails->IsWindow());
    1461           0 :   } else if (scope == GroupScope::RUNTIME) {
    1462           0 :     MOZ_ASSERT(!mDetails->IsWindow());
    1463             :   }
    1464             : #endif // defined(DEBUG)
    1465           0 :   setIsActive(mScope != GroupScope::COMPARTMENT || mService->mIsMonitoringPerCompartment);
    1466           0 : }
    1467             : 
    1468             : void
    1469           0 : nsPerformanceGroup::Dispose() {
    1470           0 :   if (!mService) {
    1471             :     // We have already called `Dispose()`.
    1472           0 :     return;
    1473             :   }
    1474           0 :   if (mObservationTarget) {
    1475           0 :     mObservationTarget = nullptr;
    1476             :   }
    1477             : 
    1478             :   // Remove any reference to the service.
    1479           0 :   RefPtr<nsPerformanceStatsService> service;
    1480           0 :   service.swap(mService);
    1481             : 
    1482             :   // Remove any dangling pointer to `this`.
    1483           0 :   service->mGroups.RemoveEntry(this);
    1484             : 
    1485           0 :   if (mScope == GroupScope::WINDOW) {
    1486           0 :     MOZ_ASSERT(mDetails->IsWindow());
    1487           0 :     service->mWindowIdToGroup.RemoveEntry(mDetails->WindowId());
    1488             :   }
    1489             : }
    1490             : 
    1491           0 : nsPerformanceGroup::~nsPerformanceGroup() {
    1492           0 :   Dispose();
    1493           0 : }
    1494             : 
    1495             : nsPerformanceGroup::GroupScope
    1496           0 : nsPerformanceGroup::Scope() const {
    1497           0 :   return mScope;
    1498             : }
    1499             : 
    1500             : nsPerformanceGroupDetails*
    1501           0 : nsPerformanceGroup::Details() const {
    1502           0 :   return mDetails;
    1503             : }
    1504             : 
    1505             : void
    1506           0 : nsPerformanceGroup::SetObservationTarget(nsPerformanceObservationTarget* target) {
    1507           0 :   MOZ_ASSERT(!mObservationTarget);
    1508           0 :   mObservationTarget = target;
    1509           0 : }
    1510             : 
    1511             : nsPerformanceObservationTarget*
    1512           0 : nsPerformanceGroup::ObservationTarget() const {
    1513           0 :   return mObservationTarget;
    1514             : }
    1515             : 
    1516             : bool
    1517           0 : nsPerformanceGroup::HasPendingAlert() const {
    1518           0 :   return mHasPendingAlert;
    1519             : }
    1520             : 
    1521             : void
    1522           0 : nsPerformanceGroup::SetHasPendingAlert(bool value) {
    1523           0 :   mHasPendingAlert = value;
    1524           0 : }
    1525             : 
    1526             : 
    1527             : void
    1528           0 : nsPerformanceGroup::RecordJank(uint64_t jank) {
    1529           0 :   if (jank > mHighestJank) {
    1530           0 :     mHighestJank = jank;
    1531             :   }
    1532           0 : }
    1533             : 
    1534             : void
    1535           0 : nsPerformanceGroup::RecordCPOW(uint64_t cpow) {
    1536           0 :   if (cpow > mHighestCPOW) {
    1537           0 :     mHighestCPOW = cpow;
    1538             :   }
    1539           0 : }
    1540             : 
    1541             : uint64_t
    1542           0 : nsPerformanceGroup::HighestRecentJank() {
    1543           0 :   return mHighestJank;
    1544             : }
    1545             : 
    1546             : uint64_t
    1547           0 : nsPerformanceGroup::HighestRecentCPOW() {
    1548           0 :   return mHighestCPOW;
    1549             : }
    1550             : 
    1551             : bool
    1552           0 : nsPerformanceGroup::HasRecentUserInput() {
    1553           0 :   return mHasRecentUserInput;
    1554             : }
    1555             : 
    1556             : void
    1557           0 : nsPerformanceGroup::RecordUserInput() {
    1558           0 :   mHasRecentUserInput = true;
    1559           0 : }
    1560             : 
    1561             : void
    1562           0 : nsPerformanceGroup::ResetRecent() {
    1563           0 :   mHighestJank = 0;
    1564           0 :   mHighestCPOW = 0;
    1565           0 :   mHasRecentUserInput = false;
    1566           0 : }

Generated by: LCOV version 1.13