From 9a0fd3701a64af42734896aff4b534a71367acef Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 17:06:15 +0200 Subject: [PATCH] Backed out changeset 00726df4997c (bug 1190074) for bustage on a CLOSED TREE --- js/src/jsapi.cpp | 6 +- js/src/jsapi.h | 48 +++++------ js/src/vm/Interpreter.cpp | 168 +++++++++++++++++++++++--------------- js/src/vm/Runtime.cpp | 87 ++++++++++---------- js/src/vm/Runtime.h | 23 ++++-- 5 files changed, 186 insertions(+), 146 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index cabd8b3771ef..8f0620a0fafe 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -368,12 +368,12 @@ IterPerformanceStats(JSContext* cx, continue; } js::AutoCompartment autoCompartment(cx, compartment); - mozilla::RefPtr ownGroup = compartment->performanceMonitoring.getOwnGroup(); + PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx); if (ownGroup->data.ticks == 0) { // Don't report compartments that have never been used. continue; } - mozilla::RefPtr sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); + PerformanceGroup* 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.getOwnGroup()->data; + *processStats = rt->stopwatch.performance; return true; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index d0ddb5ad5865..0027fee36da4 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -13,7 +13,6 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Range.h" #include "mozilla/RangedPtr.h" -#include "mozilla/RefPtr.h" #include #include @@ -5515,22 +5514,15 @@ struct PerformanceGroup { stopwatch_ = nullptr; } - // 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: + explicit PerformanceGroup(JSContext* cx, void* key); + ~PerformanceGroup() + { + MOZ_ASSERT(refCount_ == 0); + } + 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_; @@ -5542,14 +5534,20 @@ private: // The hash key for this PerformanceGroup. void* const key_; - // A reference counter. + // 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. 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_; }; // @@ -5566,7 +5564,7 @@ struct PerformanceGroupHolder { js::PerformanceGroup* getSharedGroup(JSContext*); // Get the own group. - js::PerformanceGroup* getOwnGroup(); + js::PerformanceGroup* getOwnGroup(JSContext*); // `true` if the this holder is currently associated to a shared // PerformanceGroup, `false` otherwise. Use this method to avoid @@ -5586,6 +5584,8 @@ 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`. - mozilla::RefPtr sharedGroup_; - mozilla::RefPtr ownGroup_; + js::PerformanceGroup* sharedGroup_; + js::PerformanceGroup* ownGroup_; }; /** diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index f686b70aaa56..37bbb7ff52b0 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -389,6 +389,22 @@ 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. @@ -399,20 +415,6 @@ 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 @@ -422,6 +424,9 @@ 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) @@ -437,23 +442,52 @@ class AutoStopwatch final JSRuntime* runtime = cx_->runtime(); iteration_ = runtime->stopwatch.iteration; - sharedGroup_ = acquireGroup(compartment->performanceMonitoring.getSharedGroup(cx)); - if (sharedGroup_) - topGroup_ = acquireGroup(runtime->stopwatch.performance.getOwnGroup()); + 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; + } - if (runtime->stopwatch.isMonitoringPerCompartment()) - ownGroup_ = acquireGroup(compartment->performanceMonitoring.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 (!sharedGroup_ && !ownGroup_) { + 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_) { // We are not in charge of monitoring anything. + // (isMonitoringForTop_ implies isMonitoringForGroup_, + // so we do not need to check it) return; } enter(); } - ~AutoStopwatch() - { - if (!sharedGroup_ && !ownGroup_) { + ~AutoStopwatch() { + if (!isMonitoringForGroup_ && !isMonitoringForSelf_) { // We are not in charge of monitoring anything. // (isMonitoringForTop_ implies isMonitoringForGroup_, // so we do not need to check it) @@ -471,19 +505,31 @@ 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.getOwnGroup()->data.totalCPOWTime; + CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime; isMonitoringCPOW_ = true; } @@ -517,54 +563,46 @@ 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.getOwnGroup()->data.totalCPOWTime - CPOWTimeStart_; + CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_; } commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta); } - // 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; + void commitDeltasToGroups(uint64_t userTimeDelta, + uint64_t systemTimeDelta, + uint64_t CPOWTimeDelta) + { + JSCompartment* compartment = cx_->compartment(); - if (group->hasStopwatch(iteration_)) - return nullptr; + PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); + MOZ_ASSERT(sharedGroup); + applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup->data); - group->acquireStopwatch(iteration_, this); - return group; + 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); + } } - // 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 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_); - } + void applyDeltas(uint64_t userTimeDelta, + uint64_t systemTimeDelta, + uint64_t CPOWTimeDelta, + PerformanceData& data) const { - void applyDeltas(uint64_t userTimeDelta, uint64_t systemTimeDelta, - uint64_t CPOWTimeDelta, PerformanceGroup* group) const { - if (!group) - return; - - group->data.ticks++; + data.ticks++; uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta; - group->data.totalUserTime += userTimeDelta; - group->data.totalSystemTime += systemTimeDelta; - group->data.totalCPOWTime += CPOWTimeDelta; + data.totalUserTime += userTimeDelta; + data.totalSystemTime += systemTimeDelta; + data.totalCPOWTime += CPOWTimeDelta; // Update an array containing the number of times we have missed // at least 2^0 successive ms, 2^1 successive ms, ... @@ -574,10 +612,10 @@ class AutoStopwatch final size_t i = 0; uint64_t duration = 1000; for (i = 0, duration = 1000; - i < ArrayLength(group->data.durations) && duration < totalTimeDelta; + i < ArrayLength(data.durations) && duration < totalTimeDelta; ++i, duration *= 2) { - group->data.durations[i]++; + data.durations[i]++; } } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index c579600c8401..145c1f5a9e5f 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -222,8 +222,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) largeAllocationFailureCallback(nullptr), oomCallback(nullptr), debuggerMallocSizeOf(ReturnZeroSize), - lastAnimationTime(0), - stopwatch(thisFromCtor()) + lastAnimationTime(0) { setGCStoreBufferPtr(&gc.storeBuffer); @@ -931,17 +930,40 @@ js::PerformanceGroupHolder::getHashKey(JSContext* cx) void js::PerformanceGroupHolder::unlink() { - ownGroup_ = nullptr; + if (ownGroup_) { + js_delete(ownGroup_); + ownGroup_ = nullptr; + } + + if (!sharedGroup_) { + // The group has never been instantiated. + return; + } + + js::PerformanceGroup* group = sharedGroup_; 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() +js::PerformanceGroupHolder::getOwnGroup(JSContext* cx) { if (ownGroup_) return ownGroup_; - return ownGroup_ = runtime_->new_(runtime_); + ownGroup_ = runtime_->new_(cx, nullptr); + return ownGroup_; } PerformanceGroup* @@ -963,58 +985,31 @@ js::PerformanceGroupHolder::getSharedGroup(JSContext* cx) if (!sharedGroup_) return nullptr; - runtime_->stopwatch.groups().add(ptr, key, sharedGroup_); + if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) { + js_delete(sharedGroup_); + sharedGroup_ = nullptr; + return nullptr; + } } + sharedGroup_->incRefCount(); + return sharedGroup_; } PerformanceData* js::GetPerformanceData(JSRuntime* rt) { - return &rt->stopwatch.performance.getOwnGroup()->data; + return &rt->stopwatch.performance; } -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() +js::PerformanceGroup::PerformanceGroup(JSContext* cx, void* key) + : uid(cx->runtime()->stopwatch.uniqueId()) + , stopwatch_(nullptr) + , iteration_(0) + , key_(key) + , refCount_(0) { - ++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 4b2f1e0c2981..11982419c8b7 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1512,11 +1512,6 @@ 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, @@ -1528,6 +1523,17 @@ 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 @@ -1540,9 +1546,9 @@ struct JSRuntime : public JS::shadow::Runtime, */ JSCurrentPerfGroupCallback currentPerfGroupCallback; - Stopwatch(JSRuntime* runtime) - : performance(runtime) - , iteration(0) + Stopwatch() + : iteration(0) + , isEmpty(true) , currentPerfGroupCallback(nullptr) , isMonitoringJank_(false) , isMonitoringCPOW_(false) @@ -1559,6 +1565,7 @@ struct JSRuntime : public JS::shadow::Runtime, */ void reset() { ++iteration; + isEmpty = true; } /** * Activate/deactivate stopwatch measurement of jank.