diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8f0620a0fafe..cabd8b3771ef 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -368,12 +368,12 @@ IterPerformanceStats(JSContext* cx, continue; } js::AutoCompartment autoCompartment(cx, compartment); - PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx); + mozilla::RefPtr ownGroup = compartment->performanceMonitoring.getOwnGroup(); if (ownGroup->data.ticks == 0) { // Don't report compartments that have never been used. continue; } - PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); + mozilla::RefPtr sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); if (!(*walker)(cx, ownGroup->data, ownGroup->uid, &sharedGroup->uid, closure)) { @@ -383,7 +383,7 @@ IterPerformanceStats(JSContext* cx, } // Finally, report the process stats - *processStats = rt->stopwatch.performance; + *processStats = rt->stopwatch.performance.getOwnGroup()->data; return true; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 0027fee36da4..d0ddb5ad5865 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -13,6 +13,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Range.h" #include "mozilla/RangedPtr.h" +#include "mozilla/RefPtr.h" #include #include @@ -5514,15 +5515,22 @@ struct PerformanceGroup { stopwatch_ = nullptr; } - explicit PerformanceGroup(JSContext* cx, void* key); - ~PerformanceGroup() - { - MOZ_ASSERT(refCount_ == 0); - } - private: + // Refcounting. For use with mozilla::RefPtr. + void AddRef(); + void Release(); + + // Construct a PerformanceGroup for a single compartment. + explicit PerformanceGroup(JSRuntime* rt); + + // Construct a PerformanceGroup for a group of compartments. + explicit PerformanceGroup(JSContext* rt, void* key); + +private: PerformanceGroup& operator=(const PerformanceGroup&) = delete; PerformanceGroup(const PerformanceGroup&) = delete; + JSRuntime* runtime_; + // The stopwatch currently monitoring the group, // or `nullptr` if none. Used ony for comparison. const AutoStopwatch* stopwatch_; @@ -5534,20 +5542,14 @@ struct PerformanceGroup { // The hash key for this PerformanceGroup. void* const key_; - // Increment/decrement the refcounter, return the updated value. - uint64_t incRefCount() { - MOZ_ASSERT(refCount_ + 1 > 0); - return ++refCount_; - } - uint64_t decRefCount() { - MOZ_ASSERT(refCount_ > 0); - return --refCount_; - } - friend struct PerformanceGroupHolder; - -private: - // A reference counter. Maintained by PerformanceGroupHolder. + // A reference counter. uint64_t refCount_; + + + // `true` if this PerformanceGroup may be shared by several + // compartments, `false` if it is dedicated to a single + // compartment. + const bool isSharedGroup_; }; // @@ -5564,7 +5566,7 @@ struct PerformanceGroupHolder { js::PerformanceGroup* getSharedGroup(JSContext*); // Get the own group. - js::PerformanceGroup* getOwnGroup(JSContext*); + js::PerformanceGroup* getOwnGroup(); // `true` if the this holder is currently associated to a shared // PerformanceGroup, `false` otherwise. Use this method to avoid @@ -5584,8 +5586,6 @@ struct PerformanceGroupHolder { explicit PerformanceGroupHolder(JSRuntime* runtime) : runtime_(runtime) - , sharedGroup_(nullptr) - , ownGroup_(nullptr) { } ~PerformanceGroupHolder(); @@ -5600,8 +5600,8 @@ struct PerformanceGroupHolder { // The PerformanceGroups held by this object. // Initially set to `nullptr` until the first call to `getGroup`. // May be reset to `nullptr` by a call to `unlink`. - js::PerformanceGroup* sharedGroup_; - js::PerformanceGroup* ownGroup_; + mozilla::RefPtr sharedGroup_; + mozilla::RefPtr ownGroup_; }; /** diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 37bbb7ff52b0..f686b70aaa56 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -389,22 +389,6 @@ class AutoStopwatch final // 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. @@ -415,6 +399,20 @@ class AutoStopwatch final uint64_t systemTimeStart_; uint64_t CPOWTimeStart_; + // The performance group shared by this compartment and possibly + // others, or `nullptr` if another AutoStopwatch is already in + // charge of monitoring that group. + mozilla::RefPtr sharedGroup_; + + // The toplevel group, representing the entire process, or `nullptr` + // if another AutoStopwatch is already in charge of monitoring that group. + mozilla::RefPtr topGroup_; + + // The performance group specific to this compartment, or + // `nullptr` if another AutoStopwatch is already in charge of + // monitoring that group. + mozilla::RefPtr ownGroup_; + public: // If the stopwatch is active, constructing an instance of // AutoStopwatch causes it to become the current owner of the @@ -424,9 +422,6 @@ 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) , userTimeStart_(0) @@ -442,52 +437,23 @@ class AutoStopwatch final 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. - return; - } + sharedGroup_ = acquireGroup(compartment->performanceMonitoring.getSharedGroup(cx)); + if (sharedGroup_) + topGroup_ = acquireGroup(runtime->stopwatch.performance.getOwnGroup()); - 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; - } + if (runtime->stopwatch.isMonitoringPerCompartment()) + ownGroup_ = acquireGroup(compartment->performanceMonitoring.getOwnGroup()); - 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; - } - } - - 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_); - } - - if (!isMonitoringForGroup_ && !isMonitoringForSelf_) { + if (!sharedGroup_ && !ownGroup_) { // 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_) { + ~AutoStopwatch() + { + if (!sharedGroup_ && !ownGroup_) { // We are not in charge of monitoring anything. // (isMonitoringForTop_ implies isMonitoringForGroup_, // so we do not need to check it) @@ -505,31 +471,19 @@ class AutoStopwatch final return; } + releaseGroup(sharedGroup_); + releaseGroup(topGroup_); + releaseGroup(ownGroup_); + // Finish and commit measures exit(); - - // Now release groups. - if (isMonitoringForGroup_) { - PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); - MOZ_ASSERT(sharedGroup); - sharedGroup->releaseStopwatch(iteration_, this); - } - - 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; + CPOWTimeStart_ = runtime->stopwatch.performance.getOwnGroup()->data.totalCPOWTime; isMonitoringCPOW_ = true; } @@ -563,46 +517,54 @@ class AutoStopwatch final 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_; + CPOWTimeDelta = runtime->stopwatch.performance.getOwnGroup()->data.totalCPOWTime - CPOWTimeStart_; } commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta); } - void commitDeltasToGroups(uint64_t userTimeDelta, - uint64_t systemTimeDelta, - uint64_t CPOWTimeDelta) - { - JSCompartment* compartment = cx_->compartment(); + // Attempt to acquire a group + // If the group is `null` or if the group already has a stopwatch, + // do nothing and return `null`. + // Otherwise, bind the group to `this` for the current iteration + // and return `group`. + PerformanceGroup* acquireGroup(PerformanceGroup* group) { + if (!group) + return nullptr; - PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); - MOZ_ASSERT(sharedGroup); - applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup->data); + if (group->hasStopwatch(iteration_)) + return nullptr; - 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); - } + group->acquireStopwatch(iteration_, this); + return group; } + // Release a group. + // Noop if `group` is null or if `this` is not the stopwatch + // of `group` for the current iteration. + void releaseGroup(PerformanceGroup* group) { + if (group) + group->releaseStopwatch(iteration_, this); + } - void applyDeltas(uint64_t userTimeDelta, - uint64_t systemTimeDelta, - uint64_t CPOWTimeDelta, - PerformanceData& data) const { + void commitDeltasToGroups(uint64_t userTimeDelta, uint64_t systemTimeDelta, + uint64_t CPOWTimeDelta) const { + applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup_); + applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, topGroup_); + applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, ownGroup_); + } - data.ticks++; + void applyDeltas(uint64_t userTimeDelta, uint64_t systemTimeDelta, + uint64_t CPOWTimeDelta, PerformanceGroup* group) const { + if (!group) + return; + + group->data.ticks++; uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta; - data.totalUserTime += userTimeDelta; - data.totalSystemTime += systemTimeDelta; - data.totalCPOWTime += CPOWTimeDelta; + group->data.totalUserTime += userTimeDelta; + group->data.totalSystemTime += systemTimeDelta; + group->data.totalCPOWTime += CPOWTimeDelta; // Update an array containing the number of times we have missed // at least 2^0 successive ms, 2^1 successive ms, ... @@ -612,10 +574,10 @@ class AutoStopwatch final size_t i = 0; uint64_t duration = 1000; for (i = 0, duration = 1000; - i < ArrayLength(data.durations) && duration < totalTimeDelta; + i < ArrayLength(group->data.durations) && duration < totalTimeDelta; ++i, duration *= 2) { - data.durations[i]++; + group->data.durations[i]++; } } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 145c1f5a9e5f..c579600c8401 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -222,7 +222,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) largeAllocationFailureCallback(nullptr), oomCallback(nullptr), debuggerMallocSizeOf(ReturnZeroSize), - lastAnimationTime(0) + lastAnimationTime(0), + stopwatch(thisFromCtor()) { setGCStoreBufferPtr(&gc.storeBuffer); @@ -930,40 +931,17 @@ js::PerformanceGroupHolder::getHashKey(JSContext* cx) void js::PerformanceGroupHolder::unlink() { - if (ownGroup_) { - js_delete(ownGroup_); - ownGroup_ = nullptr; - } - - if (!sharedGroup_) { - // The group has never been instantiated. - return; - } - - js::PerformanceGroup* group = sharedGroup_; + ownGroup_ = nullptr; sharedGroup_ = nullptr; - - if (group->decRefCount() > 0) { - // The group has at least another owner. - return; - } - - - JSRuntime::Stopwatch::Groups::Ptr ptr = - runtime_->stopwatch.groups().lookup(group->key_); - MOZ_ASSERT(ptr); - runtime_->stopwatch.groups().remove(ptr); - js_delete(group); } PerformanceGroup* -js::PerformanceGroupHolder::getOwnGroup(JSContext* cx) +js::PerformanceGroupHolder::getOwnGroup() { if (ownGroup_) return ownGroup_; - ownGroup_ = runtime_->new_(cx, nullptr); - return ownGroup_; + return ownGroup_ = runtime_->new_(runtime_); } PerformanceGroup* @@ -985,31 +963,58 @@ js::PerformanceGroupHolder::getSharedGroup(JSContext* cx) if (!sharedGroup_) return nullptr; - if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) { - js_delete(sharedGroup_); - sharedGroup_ = nullptr; - return nullptr; - } + runtime_->stopwatch.groups().add(ptr, key, sharedGroup_); } - sharedGroup_->incRefCount(); - return sharedGroup_; } PerformanceData* js::GetPerformanceData(JSRuntime* rt) { - return &rt->stopwatch.performance; + return &rt->stopwatch.performance.getOwnGroup()->data; } -js::PerformanceGroup::PerformanceGroup(JSContext* cx, void* key) - : uid(cx->runtime()->stopwatch.uniqueId()) - , stopwatch_(nullptr) - , iteration_(0) - , key_(key) - , refCount_(0) +js::PerformanceGroup::PerformanceGroup(JSRuntime* rt) + : uid(rt->stopwatch.uniqueId()), + runtime_(rt), + stopwatch_(nullptr), + iteration_(0), + key_(nullptr), + refCount_(0), + isSharedGroup_(false) +{ } + + js::PerformanceGroup::PerformanceGroup(JSContext* cx, void* key) + : uid(cx->runtime()->stopwatch.uniqueId()), + runtime_(cx->runtime()), + stopwatch_(nullptr), + iteration_(0), + key_(key), + refCount_(0), + isSharedGroup_(true) +{ } + +void +js::PerformanceGroup::AddRef() { + ++refCount_; +} + +void +js::PerformanceGroup::Release() +{ + MOZ_ASSERT(refCount_ > 0); + --refCount_; + if (refCount_ > 0) + return; + if (!isSharedGroup_) + return; + + JSRuntime::Stopwatch::Groups::Ptr ptr = runtime_->stopwatch.groups().lookup(key_); + MOZ_ASSERT(ptr); + runtime_->stopwatch.groups().remove(ptr); + js_delete(this); } void diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 11982419c8b7..4b2f1e0c2981 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1512,6 +1512,11 @@ struct JSRuntime : public JS::shadow::Runtime, return groups_; } + /** + * Performance data on the entire runtime. + */ + js::PerformanceGroupHolder performance; + /** * The number of times we have entered the event loop. * Used to reset counters whenever we enter the loop, @@ -1523,17 +1528,6 @@ struct JSRuntime : public JS::shadow::Runtime, */ uint64_t iteration; - /** - * `true` if no stopwatch has been registered for the - * current run of the event loop, `false` until then. - */ - bool isEmpty; - - /** - * Performance data on the entire runtime. - */ - js::PerformanceData performance; - /** * Callback used to ask the embedding to determine in which * Performance Group the current execution belongs. Typically, this is @@ -1546,9 +1540,9 @@ struct JSRuntime : public JS::shadow::Runtime, */ JSCurrentPerfGroupCallback currentPerfGroupCallback; - Stopwatch() - : iteration(0) - , isEmpty(true) + Stopwatch(JSRuntime* runtime) + : performance(runtime) + , iteration(0) , currentPerfGroupCallback(nullptr) , isMonitoringJank_(false) , isMonitoringCPOW_(false) @@ -1565,7 +1559,6 @@ struct JSRuntime : public JS::shadow::Runtime, */ void reset() { ++iteration; - isEmpty = true; } /** * Activate/deactivate stopwatch measurement of jank.