LCOV - code coverage report
Current view: top level - js/src/vm - Stopwatch.h (source / functions) Hit Total Coverage
Test: output.info Lines: 14 41 34.1 %
Date: 2017-07-14 16:53:18 Functions: 4 12 33.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       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             : #ifndef vm_Stopwatch_h
       8             : #define vm_Stopwatch_h
       9             : 
      10             : #include "mozilla/RefPtr.h"
      11             : #include "mozilla/Vector.h"
      12             : 
      13             : #include "jsapi.h"
      14             : 
      15             : /*
      16             :   An API for following in real-time the amount of CPU spent executing
      17             :   webpages, add-ons, etc.
      18             : */
      19             : 
      20             : namespace js {
      21             : 
      22             : /**
      23             :  * A container for performance groups.
      24             :  *
      25             :  * Performance monitoring deals with the execution duration of code
      26             :  * that belongs to components, for a notion of components defined by
      27             :  * the embedding.  Typically, in a web browser, a component may be a
      28             :  * webpage and/or a frame and/or a module and/or an add-on and/or a
      29             :  * sandbox and/or a process etc.
      30             :  *
      31             :  * A PerformanceGroupHolder is owned y a JSCompartment and maps that
      32             :  * compartment to all the components to which it belongs.
      33             :  */
      34             : struct PerformanceGroupHolder {
      35             : 
      36             :     /**
      37             :      * Get the groups to which this compartment belongs.
      38             :      *
      39             :      * Pre-condition: Execution must have entered the compartment.
      40             :      *
      41             :      * May return `nullptr` if the embedding has not initialized
      42             :      * support for performance groups.
      43             :      */
      44             :     const PerformanceGroupVector* getGroups(JSContext*);
      45             : 
      46         315 :     explicit PerformanceGroupHolder(JSRuntime* runtime)
      47         315 :       : runtime_(runtime)
      48         315 :       , initialized_(false)
      49         315 :     {  }
      50             :     ~PerformanceGroupHolder();
      51             :     void unlink();
      52             :   private:
      53             :     JSRuntime* runtime_;
      54             : 
      55             :     // `true` once a call to `getGroups` has succeeded.
      56             :     bool initialized_;
      57             : 
      58             :     // The groups to which this compartment belongs. Filled if and only
      59             :     // if `initialized_` is `true`.
      60             :     PerformanceGroupVector groups_;
      61             : };
      62             : 
      63             : /**
      64             :  * Container class for everything related to performance monitoring.
      65             :  */
      66           0 : struct PerformanceMonitoring {
      67             :     /**
      68             :      * The number of the current iteration of the event loop.
      69             :      */
      70       17386 :     uint64_t iteration() {
      71       17386 :         return iteration_;
      72             :     }
      73             : 
      74           4 :     explicit PerformanceMonitoring(JSRuntime* runtime)
      75           4 :       : totalCPOWTime(0)
      76             :       , stopwatchStartCallback(nullptr)
      77             :       , stopwatchStartClosure(nullptr)
      78             :       , stopwatchCommitCallback(nullptr)
      79             :       , stopwatchCommitClosure(nullptr)
      80             :       , getGroupsCallback(nullptr)
      81             :       , getGroupsClosure(nullptr)
      82             :       , isMonitoringJank_(false)
      83             :       , isMonitoringCPOW_(false)
      84             :       , iteration_(0)
      85             :       , startedAtIteration_(0)
      86           4 :       , highestTimestampCounter_(0)
      87           4 :     { }
      88             : 
      89             :     /**
      90             :      * Reset the stopwatch.
      91             :      *
      92             :      * This method is meant to be called whenever we start
      93             :      * processing an event, to ensure that we stop any ongoing
      94             :      * measurement that would otherwise provide irrelevant
      95             :      * results.
      96             :      */
      97             :     void reset();
      98             : 
      99             :     /**
     100             :      * Start the stopwatch.
     101             :      *
     102             :      * This method is meant to be called once we know that the
     103             :      * current event contains JavaScript code to execute. Calling
     104             :      * this several times during the same iteration is idempotent.
     105             :      */
     106             :     void start();
     107             : 
     108             :     /**
     109             :      * Commit the performance data collected since the last call
     110             :      * to `start()`, unless `reset()` has been called since then.
     111             :      */
     112             :     bool commit();
     113             : 
     114             :     /**
     115             :      * Liberate memory and references.
     116             :      */
     117             :     void dispose(JSRuntime* rtx);
     118             : 
     119             :     /**
     120             :      * Activate/deactivate stopwatch measurement of jank.
     121             :      *
     122             :      * Noop if `value` is `true` and the stopwatch is already
     123             :      * measuring jank, or if `value` is `false` and the stopwatch
     124             :      * is not measuring jank.
     125             :      *
     126             :      * Otherwise, any pending measurements are dropped, but previous
     127             :      * measurements remain stored.
     128             :      *
     129             :      * May return `false` if the underlying hashtable cannot be allocated.
     130             :      */
     131           0 :     bool setIsMonitoringJank(bool value) {
     132           0 :         if (isMonitoringJank_ != value)
     133           0 :             reset();
     134             : 
     135           0 :         isMonitoringJank_ = value;
     136           0 :         return true;
     137             :     }
     138           0 :     bool isMonitoringJank() const {
     139           0 :         return isMonitoringJank_;
     140             :     }
     141             : 
     142             :     /**
     143             :      * Mark that a group has been used in this iteration.
     144             :      */
     145             :     bool addRecentGroup(PerformanceGroup* group);
     146             : 
     147             :     /**
     148             :      * Activate/deactivate stopwatch measurement of CPOW.
     149             :      *
     150             :      * Noop if `value` is `true` and the stopwatch is already
     151             :      * measuring CPOW, or if `value` is `false` and the stopwatch
     152             :      * is not measuring CPOW.
     153             :      *
     154             :      * Otherwise, any pending measurements are dropped, but previous
     155             :      * measurements remain stored.
     156             :      *
     157             :      * May return `false` if the underlying hashtable cannot be allocated.
     158             :      */
     159           0 :     bool setIsMonitoringCPOW(bool value) {
     160           0 :         if (isMonitoringCPOW_ != value)
     161           0 :             reset();
     162             : 
     163           0 :         isMonitoringCPOW_ = value;
     164           0 :         return true;
     165             :     }
     166             : 
     167           0 :     bool isMonitoringCPOW() const {
     168           0 :         return isMonitoringCPOW_;
     169             :     }
     170             : 
     171             :     /**
     172             :      * Callbacks called when we start executing an event/when we have
     173             :      * run to completion (including enqueued microtasks).
     174             :      *
     175             :      * If there are no nested event loops, each call to
     176             :      * `stopwatchStartCallback` is followed by a call to
     177             :      * `stopwatchCommitCallback`. However, embedders should not assume
     178             :      * that this will always be the case, unless they take measures to
     179             :      * prevent nested event loops.
     180             :      *
     181             :      * In presence of nested event loops, several calls to
     182             :      * `stopwatchStartCallback` may occur before a call to
     183             :      * `stopwatchCommitCallback`. Embedders should assume that a
     184             :      * second call to `stopwatchStartCallback` cancels any measure
     185             :      * started by the previous calls to `stopwatchStartCallback` and
     186             :      * which have not been committed by `stopwatchCommitCallback`.
     187             :      */
     188           0 :     void setStopwatchStartCallback(js::StopwatchStartCallback cb, void* closure) {
     189           0 :         stopwatchStartCallback = cb;
     190           0 :         stopwatchStartClosure = closure;
     191           0 :     }
     192           0 :     void setStopwatchCommitCallback(js::StopwatchCommitCallback cb, void* closure) {
     193           0 :         stopwatchCommitCallback = cb;
     194           0 :         stopwatchCommitClosure = closure;
     195           0 :     }
     196             : 
     197             :     /**
     198             :      * Callback called to associate a JSCompartment to the set of
     199             :      * `PerformanceGroup`s that represent the components to which
     200             :      * it belongs.
     201             :      */
     202           0 :     void setGetGroupsCallback(js::GetGroupsCallback cb, void* closure) {
     203           0 :         getGroupsCallback = cb;
     204           0 :         getGroupsClosure = closure;
     205           0 :     }
     206             : 
     207             :     /**
     208             :      * The total amount of time spent waiting on CPOWs since the
     209             :      * start of the process, in microseconds.
     210             :      */
     211             :     uint64_t totalCPOWTime;
     212             : 
     213             :     /**
     214             :      * A variant of RDTSC artificially made monotonic.
     215             :      *
     216             :      * Always return 0 on platforms that do not support RDTSC.
     217             :      */
     218             :     uint64_t monotonicReadTimestampCounter();
     219             : 
     220             :     /**
     221             :      * Data extracted by the AutoStopwatch to determine how often
     222             :      * we reschedule the process to a different CPU during the
     223             :      * execution of JS.
     224             :      *
     225             :      * Warning: These values are incremented *only* on platforms
     226             :      * that offer a syscall/libcall to check on which CPU a
     227             :      * process is currently executed.
     228             :      */
     229             :     struct TestCpuRescheduling
     230             :     {
     231             :         // Incremented once we have finished executing code
     232             :         // in a group, if the CPU on which we started
     233             :         // execution is the same as the CPU on which
     234             :         // we finished.
     235             :         uint64_t stayed;
     236             :         // Incremented once we have finished executing code
     237             :         // in a group, if the CPU on which we started
     238             :         // execution is different from the CPU on which
     239             :         // we finished.
     240             :         uint64_t moved;
     241           4 :         TestCpuRescheduling()
     242           4 :             : stayed(0),
     243           4 :               moved(0)
     244           4 :         { }
     245             :     };
     246             :     TestCpuRescheduling testCpuRescheduling;
     247             :   private:
     248             :     PerformanceMonitoring(const PerformanceMonitoring&) = delete;
     249             :     PerformanceMonitoring& operator=(const PerformanceMonitoring&) = delete;
     250             : 
     251             :   private:
     252             :     friend struct PerformanceGroupHolder;
     253             :     js::StopwatchStartCallback stopwatchStartCallback;
     254             :     void* stopwatchStartClosure;
     255             :     js::StopwatchCommitCallback stopwatchCommitCallback;
     256             :     void* stopwatchCommitClosure;
     257             : 
     258             :     js::GetGroupsCallback getGroupsCallback;
     259             :     void* getGroupsClosure;
     260             : 
     261             :     /**
     262             :      * `true` if stopwatch monitoring is active for Jank, `false` otherwise.
     263             :      */
     264             :     bool isMonitoringJank_;
     265             :     /**
     266             :      * `true` if stopwatch monitoring is active for CPOW, `false` otherwise.
     267             :      */
     268             :     bool isMonitoringCPOW_;
     269             : 
     270             :     /**
     271             :      * The number of times we have entered the event loop.
     272             :      * Used to reset counters whenever we enter the loop,
     273             :      * which may be caused either by having completed the
     274             :      * previous run of the event loop, or by entering a
     275             :      * nested loop.
     276             :      *
     277             :      * Always incremented by 1, may safely overflow.
     278             :      */
     279             :     uint64_t iteration_;
     280             : 
     281             :     /**
     282             :      * The iteration at which the stopwatch was last started.
     283             :      *
     284             :      * Used both to avoid starting the stopwatch several times
     285             :      * during the same event loop and to avoid committing stale
     286             :      * stopwatch results.
     287             :      */
     288             :     uint64_t startedAtIteration_;
     289             : 
     290             :     /**
     291             :      * Groups used in the current iteration.
     292             :      */
     293             :     PerformanceGroupVector recentGroups_;
     294             : 
     295             :     /**
     296             :      * The highest value of the timestamp counter encountered
     297             :      * during this iteration.
     298             :      */
     299             :     uint64_t highestTimestampCounter_;
     300             : };
     301             : 
     302             : // Temporary disable untested code path.
     303             : #if 0 // WINVER >= 0x0600
     304             : struct cpuid_t {
     305             :     uint16_t group_;
     306             :     uint8_t number_;
     307             :     cpuid_t(uint16_t group, uint8_t number)
     308             :         : group_(group),
     309             :           number_(number)
     310             :     { }
     311             :     cpuid_t()
     312             :         : group_(0),
     313             :           number_(0)
     314             :     { }
     315             : };
     316             : #else
     317             :     typedef struct {} cpuid_t;
     318             : #endif // defined(WINVER >= 0x0600)
     319             : 
     320             : /**
     321             :  * RAII class to start/stop measuring performance when
     322             :  * entering/leaving a compartment.
     323             :  */
     324             : class AutoStopwatch final {
     325             :     // The context with which this object was initialized.
     326             :     // Non-null.
     327             :     JSContext* const cx_;
     328             : 
     329             :     // An indication of the number of times we have entered the event
     330             :     // loop.  Used only for comparison.
     331             :     uint64_t iteration_;
     332             : 
     333             :     // `true` if we are monitoring jank, `false` otherwise.
     334             :     bool isMonitoringJank_;
     335             :     // `true` if we are monitoring CPOW, `false` otherwise.
     336             :     bool isMonitoringCPOW_;
     337             : 
     338             :     // Timestamps captured while starting the stopwatch.
     339             :     uint64_t cyclesStart_;
     340             :     uint64_t CPOWTimeStart_;
     341             : 
     342             :     // The CPU on which we started the measure. Defined only
     343             :     // if `isMonitoringJank_` is `true`.
     344             :     cpuid_t cpuStart_;
     345             : 
     346             :     PerformanceGroupVector groups_;
     347             : 
     348             :   public:
     349             :     // If the stopwatch is active, constructing an instance of
     350             :     // AutoStopwatch causes it to become the current owner of the
     351             :     // stopwatch.
     352             :     //
     353             :     // Previous owner is restored upon destruction.
     354             :     explicit AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     355             :     ~AutoStopwatch();
     356             :   private:
     357             :     void inline enter();
     358             : 
     359             :     bool inline exit();
     360             : 
     361             :     // Attempt to acquire a group
     362             :     // If the group is inactive or if the group already has a stopwatch,
     363             :     // do nothing and return `null`.
     364             :     // Otherwise, bind the group to `this` for the current iteration
     365             :     // and return `group`.
     366             :     PerformanceGroup* acquireGroup(PerformanceGroup* group);
     367             : 
     368             :     // Release a group. Noop if `this` is not the stopwatch of
     369             :     // `group` for the current iteration.
     370             :     void releaseGroup(PerformanceGroup* group);
     371             : 
     372             :     // Add recent changes to all the groups owned by this stopwatch.
     373             :     // Mark the groups as changed recently.
     374             :     bool addToGroups(uint64_t cyclesDelta, uint64_t CPOWTimeDelta);
     375             : 
     376             :     // Add recent changes to a single group. Mark the group as changed recently.
     377             :     bool addToGroup(JSRuntime* runtime, uint64_t cyclesDelta, uint64_t CPOWTimeDelta, PerformanceGroup* group);
     378             : 
     379             :     // Update telemetry statistics.
     380             :     void updateTelemetry(const cpuid_t& a, const cpuid_t& b);
     381             : 
     382             :     // Perform a subtraction for a quantity that should be monotonic
     383             :     // but is not guaranteed to be so.
     384             :     //
     385             :     // If `start <= end`, return `end - start`.
     386             :     // Otherwise, return `0`.
     387             :     uint64_t inline getDelta(const uint64_t end, const uint64_t start) const;
     388             : 
     389             :     // Return the value of the Timestamp Counter, as provided by the CPU.
     390             :     // 0 on platforms for which we do not have access to a Timestamp Counter.
     391             :     uint64_t inline getCycles(JSRuntime*) const;
     392             : 
     393             : 
     394             :     // Return the identifier of the current CPU, on platforms for which we have
     395             :     // access to the current CPU.
     396             :     cpuid_t inline getCPU() const;
     397             : 
     398             :     // Compare two CPU identifiers.
     399             :     bool inline isSameCPU(const cpuid_t& a, const cpuid_t& b) const;
     400             :   private:
     401             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
     402             : };
     403             : 
     404             : 
     405             : } // namespace js
     406             : 
     407             : #endif // vm_Stopwatch_h

Generated by: LCOV version 1.13