diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 6451c6989f79..2305230a04b1 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -317,21 +317,16 @@ IterPerformanceStats(JSContext* cx, } JSRuntime* rt = JS_GetRuntime(cx); - - // First report the shared groups - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) { JSCompartment* compartment = c.get(); - if (!c->principals()) { - // Compartments without principals could show up here, but - // reporting them doesn't really make sense. - continue; - } - if (!c->performanceMonitoring.hasSharedGroup()) { + if (!compartment->performanceMonitoring.isLinked()) { // Don't report compartments that do not even have a PerformanceGroup. continue; } + js::AutoCompartment autoCompartment(cx, compartment); - PerformanceGroup* group = compartment->performanceMonitoring.getSharedGroup(cx); + PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx); + if (group->data.ticks == 0) { // Don't report compartments that have never been used. continue; @@ -343,9 +338,7 @@ IterPerformanceStats(JSContext* cx, continue; } - if (!(*walker)(cx, - group->data, group->uid, nullptr, - closure)) { + if (!(*walker)(cx, group->data, group->uid, closure)) { // Issue in callback return false; } @@ -354,35 +347,6 @@ IterPerformanceStats(JSContext* cx, return false; } } - - // Then report the own groups - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - JSCompartment* compartment = c.get(); - if (!c->principals()) { - // Compartments without principals could show up here, but - // reporting them doesn't really make sense. - continue; - } - if (!c->performanceMonitoring.hasOwnGroup()) { - // Don't report compartments that do not even have a PerformanceGroup. - continue; - } - js::AutoCompartment autoCompartment(cx, compartment); - PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx); - if (ownGroup->data.ticks == 0) { - // Don't report compartments that have never been used. - continue; - } - PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); - if (!(*walker)(cx, - ownGroup->data, ownGroup->uid, &sharedGroup->uid, - closure)) { - // Issue in callback - return false; - } - } - - // Finally, report the process stats *processStats = rt->stopwatch.performance; return true; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index e0f77cf733b1..7b13c55438a9 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5409,7 +5409,7 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp) namespace js { -class AutoStopwatch; +struct AutoStopwatch; // Container for performance data // All values are monotonic. @@ -5540,30 +5540,21 @@ private: }; // -// Each PerformanceGroupHolder handles: -// - a reference-counted indirection towards a PerformanceGroup shared -// by several compartments -// - a owned PerformanceGroup representing the performance of a single -// compartment. +// Indirection towards a PerformanceGroup. +// This structure handles reference counting for instances of PerformanceGroup. // struct PerformanceGroupHolder { - // Get the shared group. + // Get the group. // On first call, this causes a single Hashtable lookup. // Successive calls do not require further lookups. - js::PerformanceGroup* getSharedGroup(JSContext*); + js::PerformanceGroup* getGroup(JSContext*); - // Get the own group. - js::PerformanceGroup* getOwnGroup(JSContext*); - - // `true` if the this holder is currently associated to a shared + // `true` if the this holder is currently associated to a // PerformanceGroup, `false` otherwise. Use this method to avoid // instantiating a PerformanceGroup if you only need to get // available performance data. - inline bool hasSharedGroup() const { - return sharedGroup_ != nullptr; - } - inline bool hasOwnGroup() const { - return ownGroup_ != nullptr; + inline bool isLinked() const { + return group_ != nullptr; } // Remove the link to the PerformanceGroup. This method is designed @@ -5573,12 +5564,10 @@ struct PerformanceGroupHolder { explicit PerformanceGroupHolder(JSRuntime* runtime) : runtime_(runtime) - , sharedGroup_(nullptr) - , ownGroup_(nullptr) + , group_(nullptr) { } ~PerformanceGroupHolder(); - - private: +private: // Return the key representing this PerformanceGroup in // Runtime::Stopwatch. // Do not deallocate the key. @@ -5586,11 +5575,10 @@ struct PerformanceGroupHolder { JSRuntime *runtime_; - // The PerformanceGroups held by this object. - // Initially set to `nullptr` until the first call to `getGroup`. + // The PerformanceGroup held by this object. + // Initially set to `nullptr` until the first cal to `getGroup`. // May be reset to `nullptr` by a call to `unlink`. - js::PerformanceGroup* sharedGroup_; - js::PerformanceGroup* ownGroup_; + js::PerformanceGroup* group_; }; /** @@ -5616,10 +5604,6 @@ extern JS_PUBLIC_API(bool) SetStopwatchIsMonitoringJank(JSRuntime*, bool); extern JS_PUBLIC_API(bool) GetStopwatchIsMonitoringJank(JSRuntime*); -extern JS_PUBLIC_API(bool) -SetStopwatchIsMonitoringPerCompartment(JSRuntime*, bool); -extern JS_PUBLIC_API(bool) -GetStopwatchIsMonitoringPerCompartment(JSRuntime*); extern JS_PUBLIC_API(bool) IsStopwatchActive(JSRuntime*); @@ -5631,9 +5615,7 @@ extern JS_PUBLIC_API(PerformanceData*) GetPerformanceData(JSRuntime*); typedef bool -(PerformanceStatsWalker)(JSContext* cx, - const PerformanceData& stats, uint64_t uid, - const uint64_t* parentId, void* closure); +(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, uint64_t uid, void* closure); /** * Extract the performance statistics. diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 682e12417194..e467935df62b 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -372,49 +372,15 @@ ExecuteState::pushInterpreterFrame(JSContext* cx) scopeChain_, type_, evalInFrame_); } namespace js { + // Implementation of per-performance group performance measurement. // // // All mutable state is stored in `Runtime::stopwatch` (per-process // performance stats and logistics) and in `PerformanceGroup` (per // group performance stats). -class AutoStopwatch final +struct AutoStopwatch final { - // The context with which this object was initialized. - // Non-null. - JSContext* const cx_; - - // An indication of the number of times we have entered the event - // loop. Used only for comparison. - uint64_t iteration_; - - // `true` if this object is currently used to monitor performance - // for a shared PerformanceGroup, `false` otherwise, i.e. if the - // stopwatch mechanism is off or if another stopwatch is already - // in charge of monitoring for the same PerformanceGroup. - bool isMonitoringForGroup_; - - // `true` if this object is currently used to monitor performance - // for a single compartment, `false` otherwise, i.e. if the - // stopwatch mechanism is off or if another stopwatch is already - // in charge of monitoring for the same PerformanceGroup. - bool isMonitoringForSelf_; - - // `true` if this stopwatch is the topmost stopwatch on the stack - // for this event, `false` otherwise. - bool isMonitoringForTop_; - - // `true` if we are monitoring jank, `false` otherwise. - bool isMonitoringJank_; - // `true` if we are monitoring CPOW, `false` otherwise. - bool isMonitoringCPOW_; - - // Timestamps captured while starting the stopwatch. - uint64_t userTimeStart_; - uint64_t systemTimeStart_; - uint64_t CPOWTimeStart_; - - public: // If the stopwatch is active, constructing an instance of // AutoStopwatch causes it to become the current owner of the // stopwatch. @@ -423,198 +389,120 @@ class AutoStopwatch final explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : cx_(cx) , iteration_(0) - , isMonitoringForGroup_(false) - , isMonitoringForSelf_(false) - , isMonitoringForTop_(false) - , isMonitoringJank_(false) - , isMonitoringCPOW_(false) + , isActive_(false) + , isTop_(false) , userTimeStart_(0) , systemTimeStart_(0) , CPOWTimeStart_(0) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; + JSRuntime* runtime = JS_GetRuntime(cx_); + if (!runtime->stopwatch.isMonitoringJank()) + return; + JSCompartment* compartment = cx_->compartment(); if (compartment->scheduledForDestruction) return; - JSRuntime* runtime = cx_->runtime(); iteration_ = runtime->stopwatch.iteration; - PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); - if (!sharedGroup) { - // Either this Runtime is not configured for Performance Monitoring, or we couldn't - // allocate the group, or there was a problem with the hashtable. + PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx); + MOZ_ASSERT(group); + + if (group->hasStopwatch(iteration_)) { + // Someone is already monitoring this group during this + // tick, no need for further monitoring. return; } - if (!sharedGroup->hasStopwatch(iteration_)) { - // We are now in charge of monitoring this group for the tick, - // until destruction of `this` or until we enter a nested event - // loop and `iteration_` is incremented. - sharedGroup->acquireStopwatch(iteration_, this); - isMonitoringForGroup_ = true; - } + // Start the stopwatch. + if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_)) + return; + isActive_ = true; + CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime; - PerformanceGroup* ownGroup = nullptr; - if (runtime->stopwatch.isMonitoringPerCompartment()) { - // As above, but for the group representing just this compartment. - ownGroup = compartment->performanceMonitoring.getOwnGroup(cx); - if (!ownGroup->hasStopwatch(iteration_)) { - ownGroup->acquireStopwatch(iteration_, this); - isMonitoringForSelf_ = true; - } - } + // We are now in charge of monitoring this group for the tick, + // until destruction of `this` or until we enter a nested event + // loop and `iteration_` is incremented. + group->acquireStopwatch(iteration_, this); if (runtime->stopwatch.isEmpty) { // This is the topmost stopwatch on the stack. // It will be in charge of updating the per-process // performance data. runtime->stopwatch.isEmpty = false; - isMonitoringForTop_ = true; - - MOZ_ASSERT(isMonitoringForGroup_); + runtime->stopwatch.performance.ticks++; + isTop_ = true; } - - if (!isMonitoringForGroup_ && !isMonitoringForSelf_) { - // We are not in charge of monitoring anything. - // (isMonitoringForTop_ implies isMonitoringForGroup_, - // so we do not need to check it) - return; - } - - enter(); } - ~AutoStopwatch() { - if (!isMonitoringForGroup_ && !isMonitoringForSelf_) { + inline ~AutoStopwatch() { + if (!isActive_) { // We are not in charge of monitoring anything. - // (isMonitoringForTop_ implies isMonitoringForGroup_, - // so we do not need to check it) return; } + JSRuntime* runtime = JS_GetRuntime(cx_); JSCompartment* compartment = cx_->compartment(); - if (compartment->scheduledForDestruction) - return; - JSRuntime* runtime = cx_->runtime(); + MOZ_ASSERT(!compartment->scheduledForDestruction); + + if (!runtime->stopwatch.isMonitoringJank()) { + // Monitoring has been stopped while we were + // executing the code. Drop everything. + return; + } + if (iteration_ != runtime->stopwatch.iteration) { // We have entered a nested event loop at some point. // Any information we may have is obsolete. return; } - // Finish and commit measures - exit(); + PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx_); + MOZ_ASSERT(group); - // Now release groups. - if (isMonitoringForGroup_) { - PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); - MOZ_ASSERT(sharedGroup); - sharedGroup->releaseStopwatch(iteration_, this); - } + // Compute time spent. + group->releaseStopwatch(iteration_, this); + uint64_t userTimeEnd, systemTimeEnd; + if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd)) + return; - if (isMonitoringForSelf_) { - PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_); - MOZ_ASSERT(ownGroup); - ownGroup->releaseStopwatch(iteration_, this); - } - - if (isMonitoringForTop_) - runtime->stopwatch.isEmpty = true; - } - private: - void enter() { - JSRuntime* runtime = cx_->runtime(); - - if (runtime->stopwatch.isMonitoringCPOW()) { - CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime; - isMonitoringCPOW_ = true; - } - - if (runtime->stopwatch.isMonitoringJank()) { - if (this->getTimes(runtime, &userTimeStart_, &systemTimeStart_)) { - isMonitoringJank_ = true; - } - } - - } - - void exit() { - JSRuntime* runtime = cx_->runtime(); - - uint64_t userTimeDelta = 0; - uint64_t systemTimeDelta = 0; - if (isMonitoringJank_ && runtime->stopwatch.isMonitoringJank()) { - // We were monitoring jank when we entered and we still are. - uint64_t userTimeEnd, systemTimeEnd; - if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd)) { - // We make no attempt to recover from this error. If - // we bail out here, we lose nothing of value, plus - // I'm nearly sure that this error cannot happen in - // practice. - return; - } - userTimeDelta = userTimeEnd - userTimeStart_; - systemTimeDelta = systemTimeEnd - systemTimeStart_; - } - - uint64_t CPOWTimeDelta = 0; - if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) { - // We were monitoring CPOW when we entered and we still are. - CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_; - - } - commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta); - } - - void commitDeltasToGroups(uint64_t userTimeDelta, - uint64_t systemTimeDelta, - uint64_t CPOWTimeDelta) - { - JSCompartment* compartment = cx_->compartment(); - - PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); - MOZ_ASSERT(sharedGroup); - applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup->data); - - if (isMonitoringForSelf_) { - PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_); - MOZ_ASSERT(ownGroup); - applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, ownGroup->data); - } - - if (isMonitoringForTop_) { - JSRuntime* runtime = cx_->runtime(); - applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, runtime->stopwatch.performance); - } - } - - - void applyDeltas(uint64_t userTimeDelta, - uint64_t systemTimeDelta, - uint64_t CPOWTimeDelta, - PerformanceData& data) const { - - data.ticks++; + uint64_t userTimeDelta = userTimeEnd - userTimeStart_; + uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_; + uint64_t CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_; + group->data.totalUserTime += userTimeDelta; + group->data.totalSystemTime += systemTimeDelta; + group->data.totalCPOWTime += CPOWTimeDelta; uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta; - data.totalUserTime += userTimeDelta; - data.totalSystemTime += systemTimeDelta; - data.totalCPOWTime += CPOWTimeDelta; + updateDurations(totalTimeDelta, group->data.durations); + group->data.ticks++; - // Update an array containing the number of times we have missed - // at least 2^0 successive ms, 2^1 successive ms, ... - // 2^i successive ms. + if (isTop_) { + // This is the topmost stopwatch on the stack. + // Record the timing information. + runtime->stopwatch.performance.totalUserTime = userTimeEnd; + runtime->stopwatch.performance.totalSystemTime = systemTimeEnd; + updateDurations(totalTimeDelta, runtime->stopwatch.performance.durations); + runtime->stopwatch.isEmpty = true; + } + } + private: + + // Update an array containing the number of times we have missed + // at least 2^0 successive ms, 2^1 successive ms, ... + // 2^i successive ms. + template + void updateDurations(uint64_t totalTimeDelta, uint64_t (&array)[N]) const { // Duration of one frame, i.e. 16ms in museconds size_t i = 0; uint64_t duration = 1000; for (i = 0, duration = 1000; - i < ArrayLength(data.durations) && duration < totalTimeDelta; - ++i, duration *= 2) - { - data.durations[i]++; + i < N && duration < totalTimeDelta; + ++i, duration *= 2) { + array[i]++; } } @@ -697,9 +585,31 @@ class AutoStopwatch final return true; } + private: + // The context with which this object was initialized. + // Non-null. + JSContext* const cx_; -private: - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; + // An indication of the number of times we have entered the event + // loop. Used only for comparison. + uint64_t iteration_; + + // `true` if this object is currently used to monitor performance, + // `false` otherwise, i.e. if the stopwatch mechanism is off or if + // another stopwatch is already in charge of monitoring for the + // same PerformanceGroup. + bool isActive_; + + // `true` if this stopwatch is the topmost stopwatch on the stack + // for this event, `false` otherwise. + bool isTop_; + + // Timestamps captured while starting the stopwatch. + uint64_t userTimeStart_; + uint64_t systemTimeStart_; + uint64_t CPOWTimeStart_; + + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; } // namespace js diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 522835c32092..15e7c7b93019 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -899,17 +899,6 @@ js::GetStopwatchIsMonitoringCPOW(JSRuntime* rt) return rt->stopwatch.isMonitoringCPOW(); } -bool -js::SetStopwatchIsMonitoringPerCompartment(JSRuntime* rt, bool value) -{ - return rt->stopwatch.setIsMonitoringPerCompartment(value); -} -bool -js::GetStopwatchIsMonitoringPerCompartment(JSRuntime* rt) -{ - return rt->stopwatch.isMonitoringPerCompartment(); -} - js::PerformanceGroupHolder::~PerformanceGroupHolder() { unlink(); @@ -929,18 +918,13 @@ js::PerformanceGroupHolder::getHashKey(JSContext* cx) void js::PerformanceGroupHolder::unlink() { - if (ownGroup_) { - js_delete(ownGroup_); - ownGroup_ = nullptr; - } - - if (!sharedGroup_) { + if (!group_) { // The group has never been instantiated. return; } - js::PerformanceGroup* group = sharedGroup_; - sharedGroup_ = nullptr; + js::PerformanceGroup* group = group_; + group_ = nullptr; if (group->decRefCount() > 0) { // The group has at least another owner. @@ -949,51 +933,32 @@ js::PerformanceGroupHolder::unlink() JSRuntime::Stopwatch::Groups::Ptr ptr = - runtime_->stopwatch.groups().lookup(group->key_); + runtime_->stopwatch.groups_.lookup(group->key_); MOZ_ASSERT(ptr); - runtime_->stopwatch.groups().remove(ptr); + runtime_->stopwatch.groups_.remove(ptr); js_delete(group); } PerformanceGroup* -js::PerformanceGroupHolder::getOwnGroup(JSContext* cx) +js::PerformanceGroupHolder::getGroup(JSContext* cx) { - if (ownGroup_) - return ownGroup_; - - ownGroup_ = runtime_->new_(cx, nullptr); - return ownGroup_; -} - -PerformanceGroup* -js::PerformanceGroupHolder::getSharedGroup(JSContext* cx) -{ - if (sharedGroup_) - return sharedGroup_; - - if (!runtime_->stopwatch.groups().initialized()) - return nullptr; + if (group_) + return group_; void* key = getHashKey(cx); - JSRuntime::Stopwatch::Groups::AddPtr ptr = runtime_->stopwatch.groups().lookupForAdd(key); + JSRuntime::Stopwatch::Groups::AddPtr ptr = + runtime_->stopwatch.groups_.lookupForAdd(key); if (ptr) { - sharedGroup_ = ptr->value(); - MOZ_ASSERT(sharedGroup_); + group_ = ptr->value(); + MOZ_ASSERT(group_); } else { - sharedGroup_ = runtime_->new_(cx, key); - if (!sharedGroup_) - return nullptr; - - if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) { - js_delete(sharedGroup_); - sharedGroup_ = nullptr; - return nullptr; - } + group_ = runtime_->new_(cx, key); + runtime_->stopwatch.groups_.add(ptr, key, group_); } - sharedGroup_->incRefCount(); + group_->incRefCount(); - return sharedGroup_; + return group_; } PerformanceData* diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 36f9375c5f30..0e72361fd48e 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1483,29 +1483,6 @@ struct JSRuntime : public JS::shadow::Runtime, Performance measurements ------------------------------------------ */ struct Stopwatch { - /** - * A map used to collapse compartments belonging to the same - * add-on (respectively to the same webpage, to the platform) - * into a single group. - * - * Keys: for system compartments, a `JSAddonId*` (which may be - * `nullptr`), and for webpages, a `JSPrincipals*` (which may - * not). Note that compartments may start as non-system - * compartments and become compartments later during their - * lifetime, which requires an invalidation. - * - * This map is meant to be accessed only by instances of - * PerformanceGroupHolder, which handle both reference-counting - * of the values and invalidation of the key/value pairs. - */ - typedef js::HashMap, - js::SystemAllocPolicy> Groups; - - Groups& groups() { - return groups_; - } - /** * The number of times we have entered the event loop. * Used to reset counters whenever we enter the loop, @@ -1546,7 +1523,6 @@ struct JSRuntime : public JS::shadow::Runtime, , currentPerfGroupCallback(nullptr) , isMonitoringJank_(false) , isMonitoringCPOW_(false) - , isMonitoringPerCompartment_(false) , idCounter_(0) { } @@ -1588,21 +1564,6 @@ struct JSRuntime : public JS::shadow::Runtime, return isMonitoringJank_; } - bool setIsMonitoringPerCompartment(bool value) { - if (isMonitoringPerCompartment_ != value) - reset(); - - if (value && !groups_.initialized()) { - if (!groups_.init(128)) - return false; - } - - isMonitoringPerCompartment_ = value; - return true; - } - bool isMonitoringPerCompartment() const { - return isMonitoringPerCompartment_; - } /** * Activate/deactivate stopwatch measurement of CPOW. @@ -1646,6 +1607,25 @@ struct JSRuntime : public JS::shadow::Runtime, MonotonicTimeStamp userTimeFix; private: + /** + * A map used to collapse compartments belonging to the same + * add-on (respectively to the same webpage, to the platform) + * into a single group. + * + * Keys: for system compartments, a `JSAddonId*` (which may be + * `nullptr`), and for webpages, a `JSPrincipals*` (which may + * not). Note that compartments may start as non-system + * compartments and become compartments later during their + * lifetime, which requires an invalidation. + * + * This map is meant to be accessed only by instances of + * PerformanceGroupHolder, which handle both reference-counting + * of the values and invalidation of the key/value pairs. + */ + typedef js::HashMap, + js::SystemAllocPolicy> Groups; + Groups groups_; friend struct js::PerformanceGroupHolder; @@ -1654,7 +1634,6 @@ struct JSRuntime : public JS::shadow::Runtime, */ bool isMonitoringJank_; bool isMonitoringCPOW_; - bool isMonitoringPerCompartment_; /** * A counter used to generate unique identifiers for groups. diff --git a/toolkit/components/perfmonitoring/PerformanceStats.jsm b/toolkit/components/perfmonitoring/PerformanceStats.jsm index 776ba7b70c8f..6b2ca3e57db2 100644 --- a/toolkit/components/perfmonitoring/PerformanceStats.jsm +++ b/toolkit/components/perfmonitoring/PerformanceStats.jsm @@ -146,16 +146,6 @@ Probe.prototype = { return this._impl.subtract(a, b); }, - importChildCompartments: function(parent, children) { - if (!Array.isArray(children)) { - throw new TypeError(); - } - if (!parent || !(parent instanceof PerformanceData)) { - throw new TypeError(); - } - return this._impl.importChildCompartments(parent, children); - }, - /** * The name of the probe. */ @@ -238,8 +228,7 @@ let Probes = { } result.longestDuration = lastNonZero(result.durations); return result; - }, - importChildCompartments: function() { /* nothing to do */ }, + } }), /** @@ -269,8 +258,7 @@ let Probes = { return { totalCPOWTime: a.totalCPOWTime - b.totalCPOWTime }; - }, - importChildCompartments: function() { /* nothing to do */ }, + } }), /** @@ -299,8 +287,7 @@ let Probes = { return { ticks: a.ticks - b.ticks }; - }, - importChildCompartments: function() { /* nothing to do */ }, + } }), "jank-content": new Probe("jank-content", { @@ -324,8 +311,7 @@ let Probes = { }, subtract: function(a, b) { return null; - }, - importChildCompartments: function() { /* nothing to do */ }, + } }), "cpow-content": new Probe("cpow-content", { @@ -349,22 +335,15 @@ let Probes = { }, subtract: function(a, b) { return null; - }, - importChildCompartments: function() { /* nothing to do */ }, + } }), "ticks-content": new Probe("ticks-content", { - _isActive: false, set isActive(x) { - this._isActive = x; - if (x) { - Process.broadcast("acquire", ["ticks"]); - } else { - Process.broadcast("release", ["ticks"]); - } + // Ignore: This probe is always active. }, get isActive() { - return this._isActive; + return true; }, extract: function(xpcom) { return {}; @@ -374,30 +353,8 @@ let Probes = { }, subtract: function(a, b) { return null; - }, - importChildCompartments: function() { /* nothing to do */ }, + } }), - - compartments: new Probe("compartments", { - set isActive(x) { - performanceStatsService.isMonitoringPerCompartment = x; - }, - get isActive() { - return performanceStatsService.isMonitoringPerCompartment; - }, - extract: function(xpcom) { - return null; - }, - isEqual: function(a, b) { - return true; - }, - subtract: function(a, b) { - return true; - }, - importChildCompartments: function(parent, children) { - parent.children = children; - }, - }) }; @@ -700,32 +657,16 @@ function PerformanceDiff(current, old = null) { function Snapshot({xpcom, childProcesses, probes}) { this.componentsData = []; - // Current process + // Parent process if (xpcom) { - let children = new Map(); let enumeration = xpcom.getComponentsData().enumerate(); while (enumeration.hasMoreElements()) { let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats); - let stat = new PerformanceData({xpcom, probes}); - if (!stat.parentId) { - this.componentsData.push(stat); - } else { - let siblings = children.get(stat.parentId); - if (!siblings) { - children.set(stat.parentId, (siblings = [])); - } - siblings.push(stat); - } - } - - for (let parent of this.componentsData) { - for (let probe of probes) { - probe.importChildCompartments(parent, children.get(parent.groupId) || []); - } + this.componentsData.push(new PerformanceData({xpcom, probes})); } } - // Child processes + // Content processes if (childProcesses) { for (let {componentsData} of childProcesses) { // We are only interested in `componentsData` for the time being. diff --git a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl index dc1d11ba84a0..272178e253d5 100644 --- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl +++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl @@ -17,12 +17,12 @@ /** * Snapshot of the performance of a component, e.g. an add-on, a web - * page, system built-ins, a module or the entire process itself. + * page, system built-ins, or the entire process itself. * * All values are monotonic and are updated only when * `nsIPerformanceStatsService.isStopwatchActive` is `true`. */ -[scriptable, uuid(1bc2d016-e9ae-4186-97c6-9478eddda245)] +[scriptable, uuid(47f8d36d-1d67-43cb-befd-d2f4720ac568)] interface nsIPerformanceStats: nsISupports { /** * An identifier unique to the component. @@ -32,16 +32,6 @@ interface nsIPerformanceStats: nsISupports { */ readonly attribute AString groupId; - /** - * If this component is part of a larger component, the larger - * component. Otherwise, null. - * - * As of this writing, there can be at most two levels of components: - * - compartments (a single module, iframe, etc.); - * - groups (an entire add-on, an entire webpage, etc.). - */ - readonly attribute AString parentId; - /** * The name of the component: * - for the process itself, ""; @@ -122,7 +112,7 @@ interface nsIPerformanceSnapshot: nsISupports { nsIPerformanceStats getProcessData(); }; -[scriptable, builtinclass, uuid(60973d54-13e2-455c-a3c6-84dea5dfc8b9)] +[scriptable, builtinclass, uuid(0469e6af-95c3-4961-a385-4bc009128939)] interface nsIPerformanceStatsService : nsISupports { /** * `true` if we should monitor CPOW, `false` otherwise. @@ -134,13 +124,6 @@ interface nsIPerformanceStatsService : nsISupports { */ [implicit_jscontext] attribute bool isMonitoringJank; - /** - * `true` if all compartments need to be monitored individually, - * `false` if only performance groups (i.e. entire add-ons, entire - * webpages, etc.) need to be monitored. - */ - [implicit_jscontext] attribute bool isMonitoringPerCompartment; - /** * Capture a snapshot of the performance data. */ diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp index 16ddfe28ae31..960b238ab045 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp @@ -30,7 +30,6 @@ class nsPerformanceStats: public nsIPerformanceStats { public: nsPerformanceStats(const nsAString& aName, - nsIPerformanceStats* aParent, const nsAString& aGroupId, const nsAString& aAddonId, const nsAString& aTitle, @@ -45,10 +44,6 @@ public: , mIsSystem(aIsSystem) , mPerformanceData(aPerformanceData) { - if (aParent) { - mozilla::DebugOnly rv = aParent->GetGroupId(mParentId); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - } } explicit nsPerformanceStats() {} @@ -66,12 +61,6 @@ public: return NS_OK; }; - /* readonly attribute AString parentId; */ - NS_IMETHOD GetParentId(nsAString& aParentId) override { - aParentId.Assign(mParentId); - return NS_OK; - }; - /* readonly attribute AString addonId; */ NS_IMETHOD GetAddonId(nsAString& aAddonId) override { aAddonId.Assign(mAddonId); @@ -135,7 +124,6 @@ public: private: nsString mName; - nsString mParentId; nsString mGroupId; nsString mAddonId; nsString mTitle; @@ -171,18 +159,13 @@ private: * entire process, rather than the statistics for a specific set of * compartments. */ - already_AddRefed ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid, nsIPerformanceStats* parent); + already_AddRefed ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid); /** * Callbacks for iterating through the `PerformanceStats` of a runtime. */ - bool IterPerformanceStatsCallbackInternal(JSContext* cx, - const js::PerformanceData& ownStats, const uint64_t ownId, - const uint64_t* parentId); - static bool IterPerformanceStatsCallback(JSContext* cx, - const js::PerformanceData& ownStats, const uint64_t ownId, - const uint64_t* parentId, - void* self); + bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, uint64_t uid); + static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, uint64_t uid, void* self); // If the context represents a window, extract the title and window ID. // Otherwise, extract "" and 0. @@ -192,11 +175,6 @@ private: void GetGroupId(JSContext*, uint64_t uid, nsString& groupId); - - static void GetName(JSContext*, - JS::Handle global, - nsString& name); - // If the context presents an add-on, extract the addon ID. // Otherwise, extract "". static void GetAddonId(JSContext*, @@ -210,7 +188,6 @@ private: private: nsCOMArray mComponentsData; nsCOMPtr mProcessData; - nsBaseHashtable, nsCOMPtr > mCachedStats; uint64_t mProcessId; }; @@ -296,44 +273,8 @@ nsPerformanceSnapshot::GetIsSystem(JSContext*, return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global)); } -/* static */ -void -nsPerformanceSnapshot::GetName(JSContext* cx, - JS::Handle global, - nsString& name) -{ - nsAutoCString cname; - do { - // Attempt to use the URL as name. - nsCOMPtr principal = nsContentUtils::ObjectPrincipal(global); - if (!principal) { - break; - } - - nsCOMPtr uri; - nsresult rv = principal->GetURI(getter_AddRefs(uri)); - if (NS_FAILED(rv) || !uri) { - break; - } - - rv = uri->GetSpec(cname); - if (NS_FAILED(rv)) { - break; - } - - name.Assign(NS_ConvertUTF8toUTF16(cname)); - return; - } while(false); - xpc::GetCurrentCompartmentName(cx, cname); - name.Assign(NS_ConvertUTF8toUTF16(cname)); -} - already_AddRefed -nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid, nsIPerformanceStats* parent) { - if (performance.ticks == 0) { - // Ignore compartments with no activity. - return nullptr; - } +nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid) { JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); if (!global) { @@ -352,34 +293,28 @@ nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& per uint64_t windowId; GetWindowData(cx, title, &windowId); - nsString name; - GetName(cx, global, name); + nsAutoString name; + nsAutoCString cname; + xpc::GetCurrentCompartmentName(cx, cname); + name.Assign(NS_ConvertUTF8toUTF16(cname)); bool isSystem = GetIsSystem(cx, global); nsCOMPtr result = - new nsPerformanceStats(name, parent, groupId, addonId, title, windowId, isSystem, performance); + new nsPerformanceStats(name, groupId, addonId, title, windowId, isSystem, performance); return result.forget(); } /*static*/ bool -nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, - const js::PerformanceData& stats, const uint64_t id, - const uint64_t* parentId, - void* self) { - return reinterpret_cast(self)->IterPerformanceStatsCallbackInternal(cx, stats, id, parentId); +nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid, void* self) { + return reinterpret_cast(self)->IterPerformanceStatsCallbackInternal(cx, stats, uid); } bool -nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, - const js::PerformanceData& stats, const uint64_t id, - const uint64_t* parentId) { - - nsCOMPtr parent = parentId ? mCachedStats.Get(*parentId) : nullptr; - nsCOMPtr result = ImportStats(cx, stats, id, parent); +nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid) { + nsCOMPtr result = ImportStats(cx, stats, uid); if (result) { mComponentsData.AppendElement(result); - mCachedStats.Put(id, result); } return true; @@ -393,12 +328,8 @@ nsPerformanceSnapshot::Init(JSContext* cx, uint64_t processId) { return NS_ERROR_UNEXPECTED; } - nsAutoString processGroupId; - processGroupId.AssignLiteral("process: "); - processGroupId.AppendInt(processId); mProcessData = new nsPerformanceStats(NS_LITERAL_STRING(""), // name - nullptr, // parent - processGroupId, // group id + NS_LITERAL_STRING(""), // group id NS_LITERAL_STRING(""), // add-on id NS_LITERAL_STRING(""), // title 0, // window id @@ -474,20 +405,6 @@ NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool } return NS_OK; } -NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext* cx, bool *aIsStopwatchActive) -{ - JSRuntime *runtime = JS_GetRuntime(cx); - *aIsStopwatchActive = js::GetStopwatchIsMonitoringPerCompartment(runtime); - return NS_OK; -} -NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext* cx, bool aIsStopwatchActive) -{ - JSRuntime *runtime = JS_GetRuntime(cx); - if (!js::SetStopwatchIsMonitoringPerCompartment(runtime, aIsStopwatchActive)) { - return NS_ERROR_OUT_OF_MEMORY; - } - return NS_OK; -} /* readonly attribute nsIPerformanceSnapshot snapshot; */ NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot) diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js index 4492a4c55cf2..360b6a1f9b64 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js @@ -28,7 +28,7 @@ function frameScript() { getService(Ci.nsIPerformanceStatsService); // Make sure that the stopwatch is now active. - let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks", "compartments"]); + let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]); addMessageListener("compartments-test:getStatistics", () => { try { @@ -158,10 +158,8 @@ function monotinicity_tester(source, testName) { ["jank", "totalSystemTime"], ["cpow", "totalCPOWTime"] ]) { - // Note that we cannot expect components data to be always smaller - // than process data, as `getrusage` & co are not monotonic. - SilentAssert.leq(item[probe][k], 2 * snapshot.processData[probe][k], - `Sanity check (${testName}): ${k} of component is not impossibly larger than that of process`); + SilentAssert.leq(item[probe][k], snapshot.processData[probe][k], + `Sanity check (${testName}): component has a lower ${k} than process`); } let key = item.groupId; @@ -171,24 +169,6 @@ function monotinicity_tester(source, testName) { } map.set(key, item); } - for (let item of snapshot.componentsData) { - if (!item.parentId) { - continue; - } - let parent = map.get(item.parentId); - SilentAssert.ok(parent, `The parent exists ${item.parentId}`); - - for (let [probe, k] of [ - ["jank", "totalUserTime"], - ["jank", "totalSystemTime"], - ["cpow", "totalCPOWTime"] - ]) { - // Note that we cannot expect components data to be always smaller - // than parent data, as `getrusage` & co are not monotonic. - SilentAssert.leq(item[probe][k], 2 * parent[probe][k], - `Sanity check (${testName}): ${k} of component is not impossibly larger than that of parent`); - } - } for (let [key, item] of map) { sanityCheck(previous.componentsMap.get(key), item); previous.componentsMap.set(key, item); diff --git a/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js b/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js index f4c7d562f38b..44a10a08ba3c 100644 --- a/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js +++ b/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js @@ -19,12 +19,10 @@ let promiseStatistics = Task.async(function*(name) { let componentsData = []; let componentsEnum = snapshot.getComponentsData().enumerate(); while (componentsEnum.hasMoreElements()) { - let data = componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats); - let normalized = JSON.parse(JSON.stringify(data)); - componentsData.push(data); + componentsData.push(componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats)); } return { - processData: JSON.parse(JSON.stringify(snapshot.getProcessData())), + processData: snapshot.getProcessData(), componentsData }; }); @@ -37,19 +35,11 @@ let promiseSetMonitoring = Task.async(function*(to) { yield Promise.resolve(); }); -let promiseSetPerCompartment = Task.async(function*(to) { - let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"]. - getService(Ci.nsIPerformanceStatsService); - service.isMonitoringPerCompartment = to; - yield Promise.resolve(); -}); - function getBuiltinStatistics(name, snapshot) { let stats = snapshot.componentsData.find(stats => stats.isSystem && !stats.addonId ); do_print(`Built-in statistics for ${name} were ${stats?"":"not "}found`); - do_print(JSON.stringify(snapshot.componentsData, null, "\t")); return stats; } @@ -67,25 +57,17 @@ function burnCPU(ms) { } function ensureEquals(snap1, snap2, name) { - for (let k of Object.keys(snap1.processData)) { - if (k == "ticks") { - // Ticks monitoring cannot be deactivated - continue; - } - Assert.equal(snap1.processData[k], snap2.processData[k], `Same process data value ${k} (${name})`) - } + Assert.equal( + JSON.stringify(snap1.processData), + JSON.stringify(snap2.processData), + "Same process data: " + name); let stats1 = snap1.componentsData.sort((a, b) => a.name <= b.name); let stats2 = snap2.componentsData.sort((a, b) => a.name <= b.name); - Assert.equal(stats1.length, stats2.length, `Same number of components (${name})`); - for (let i = 0; i < stats1.length; ++i) { - for (let k of Object.keys(stats1[i])) { - if (k == "ticks") { - // Ticks monitoring cannot be deactivated - continue; - } - Assert.equal(stats1[i][k], stats1[i][k], `Same component data value ${i} ${k} (${name})`) - } - } + Assert.equal( + JSON.stringify(stats1), + JSON.stringify(stats2), + "Same components data: " + name + ); } function hasLowPrecision() { @@ -106,7 +88,6 @@ function hasLowPrecision() { add_task(function* test_measure() { let skipPrecisionTests = hasLowPrecision(); - yield promiseSetPerCompartment(false); do_print("Burn CPU without the stopwatch"); yield promiseSetMonitoring(false); @@ -156,14 +137,4 @@ add_task(function* test_measure() { Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "No CPOW for built-in statistics"); Assert.equal(builtin4.totalUserTime, builtin3.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in"); Assert.equal(builtin4.totalCPOWTime, builtin3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in"); - - // Ideally, we should be able to look for test_compartments.js, but - // it doesn't have its own compartment. - for (let stats of [stats1, stats2, stats3, stats4]) { - Assert.ok(!stats.componentsData.find(x => x.name.includes("Task.jsm")), "At this stage, Task.jsm doesn't show up in the components data"); - } - yield promiseSetPerCompartment(true); - burnCPU(300); - let stats5 = yield promiseStatistics("With per-compartment monitoring"); - Assert.ok(stats5.componentsData.find(x => x.name.includes("Task.jsm")), "With per-compartment monitoring, test_compartments.js shows up"); });