Bug 1190074 - PerformanceGroup now uses mozilla::RefPtr. r=jandem

This commit is contained in:
David Rajchenbach-Teller 2015-07-27 23:01:42 +02:00
parent 280c82e934
commit d0a2a9c530
5 changed files with 151 additions and 186 deletions

View File

@ -368,12 +368,12 @@ IterPerformanceStats(JSContext* cx,
continue; continue;
} }
js::AutoCompartment autoCompartment(cx, compartment); js::AutoCompartment autoCompartment(cx, compartment);
PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx); mozilla::RefPtr<PerformanceGroup> ownGroup = compartment->performanceMonitoring.getOwnGroup();
if (ownGroup->data.ticks == 0) { if (ownGroup->data.ticks == 0) {
// Don't report compartments that have never been used. // Don't report compartments that have never been used.
continue; continue;
} }
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); mozilla::RefPtr<PerformanceGroup> sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx);
if (!(*walker)(cx, if (!(*walker)(cx,
ownGroup->data, ownGroup->uid, &sharedGroup->uid, ownGroup->data, ownGroup->uid, &sharedGroup->uid,
closure)) { closure)) {
@ -383,7 +383,7 @@ IterPerformanceStats(JSContext* cx,
} }
// Finally, report the process stats // Finally, report the process stats
*processStats = rt->stopwatch.performance; *processStats = rt->stopwatch.performance.getOwnGroup()->data;
return true; return true;
} }

View File

@ -13,6 +13,7 @@
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/Range.h" #include "mozilla/Range.h"
#include "mozilla/RangedPtr.h" #include "mozilla/RangedPtr.h"
#include "mozilla/RefPtr.h"
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
@ -5514,15 +5515,22 @@ struct PerformanceGroup {
stopwatch_ = nullptr; stopwatch_ = nullptr;
} }
explicit PerformanceGroup(JSContext* cx, void* key); // Refcounting. For use with mozilla::RefPtr.
~PerformanceGroup() void AddRef();
{ void Release();
MOZ_ASSERT(refCount_ == 0);
} // Construct a PerformanceGroup for a single compartment.
private: 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& operator=(const PerformanceGroup&) = delete;
PerformanceGroup(const PerformanceGroup&) = delete; PerformanceGroup(const PerformanceGroup&) = delete;
JSRuntime* runtime_;
// The stopwatch currently monitoring the group, // The stopwatch currently monitoring the group,
// or `nullptr` if none. Used ony for comparison. // or `nullptr` if none. Used ony for comparison.
const AutoStopwatch* stopwatch_; const AutoStopwatch* stopwatch_;
@ -5534,20 +5542,14 @@ struct PerformanceGroup {
// The hash key for this PerformanceGroup. // The hash key for this PerformanceGroup.
void* const key_; void* const key_;
// Increment/decrement the refcounter, return the updated value. // A reference counter.
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_; 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*); js::PerformanceGroup* getSharedGroup(JSContext*);
// Get the own group. // Get the own group.
js::PerformanceGroup* getOwnGroup(JSContext*); js::PerformanceGroup* getOwnGroup();
// `true` if the this holder is currently associated to a shared // `true` if the this holder is currently associated to a shared
// PerformanceGroup, `false` otherwise. Use this method to avoid // PerformanceGroup, `false` otherwise. Use this method to avoid
@ -5584,8 +5586,6 @@ struct PerformanceGroupHolder {
explicit PerformanceGroupHolder(JSRuntime* runtime) explicit PerformanceGroupHolder(JSRuntime* runtime)
: runtime_(runtime) : runtime_(runtime)
, sharedGroup_(nullptr)
, ownGroup_(nullptr)
{ } { }
~PerformanceGroupHolder(); ~PerformanceGroupHolder();
@ -5600,8 +5600,8 @@ struct PerformanceGroupHolder {
// The PerformanceGroups held by this object. // The PerformanceGroups held by this object.
// Initially set to `nullptr` until the first call to `getGroup`. // Initially set to `nullptr` until the first call to `getGroup`.
// May be reset to `nullptr` by a call to `unlink`. // May be reset to `nullptr` by a call to `unlink`.
js::PerformanceGroup* sharedGroup_; mozilla::RefPtr<js::PerformanceGroup> sharedGroup_;
js::PerformanceGroup* ownGroup_; mozilla::RefPtr<js::PerformanceGroup> ownGroup_;
}; };
/** /**

View File

@ -389,22 +389,6 @@ class AutoStopwatch final
// loop. Used only for comparison. // loop. Used only for comparison.
uint64_t iteration_; 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. // `true` if we are monitoring jank, `false` otherwise.
bool isMonitoringJank_; bool isMonitoringJank_;
// `true` if we are monitoring CPOW, `false` otherwise. // `true` if we are monitoring CPOW, `false` otherwise.
@ -415,6 +399,20 @@ class AutoStopwatch final
uint64_t systemTimeStart_; uint64_t systemTimeStart_;
uint64_t CPOWTimeStart_; 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<js::PerformanceGroup> sharedGroup_;
// The toplevel group, representing the entire process, or `nullptr`
// if another AutoStopwatch is already in charge of monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> topGroup_;
// The performance group specific to this compartment, or
// `nullptr` if another AutoStopwatch is already in charge of
// monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> ownGroup_;
public: public:
// If the stopwatch is active, constructing an instance of // If the stopwatch is active, constructing an instance of
// AutoStopwatch causes it to become the current owner of the // 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) explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: cx_(cx) : cx_(cx)
, iteration_(0) , iteration_(0)
, isMonitoringForGroup_(false)
, isMonitoringForSelf_(false)
, isMonitoringForTop_(false)
, isMonitoringJank_(false) , isMonitoringJank_(false)
, isMonitoringCPOW_(false) , isMonitoringCPOW_(false)
, userTimeStart_(0) , userTimeStart_(0)
@ -442,52 +437,23 @@ class AutoStopwatch final
JSRuntime* runtime = cx_->runtime(); JSRuntime* runtime = cx_->runtime();
iteration_ = runtime->stopwatch.iteration; iteration_ = runtime->stopwatch.iteration;
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); sharedGroup_ = acquireGroup(compartment->performanceMonitoring.getSharedGroup(cx));
if (!sharedGroup) { if (sharedGroup_)
// Either this Runtime is not configured for Performance Monitoring, or we couldn't topGroup_ = acquireGroup(runtime->stopwatch.performance.getOwnGroup());
// allocate the group, or there was a problem with the hashtable.
return;
}
if (!sharedGroup->hasStopwatch(iteration_)) { if (runtime->stopwatch.isMonitoringPerCompartment())
// We are now in charge of monitoring this group for the tick, ownGroup_ = acquireGroup(compartment->performanceMonitoring.getOwnGroup());
// until destruction of `this` or until we enter a nested event
// loop and `iteration_` is incremented.
sharedGroup->acquireStopwatch(iteration_, this);
isMonitoringForGroup_ = true;
}
PerformanceGroup* ownGroup = nullptr; if (!sharedGroup_ && !ownGroup_) {
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. // We are not in charge of monitoring anything.
// (isMonitoringForTop_ implies isMonitoringForGroup_,
// so we do not need to check it)
return; return;
} }
enter(); enter();
} }
~AutoStopwatch() { ~AutoStopwatch()
if (!isMonitoringForGroup_ && !isMonitoringForSelf_) { {
if (!sharedGroup_ && !ownGroup_) {
// We are not in charge of monitoring anything. // We are not in charge of monitoring anything.
// (isMonitoringForTop_ implies isMonitoringForGroup_, // (isMonitoringForTop_ implies isMonitoringForGroup_,
// so we do not need to check it) // so we do not need to check it)
@ -505,31 +471,19 @@ class AutoStopwatch final
return; return;
} }
releaseGroup(sharedGroup_);
releaseGroup(topGroup_);
releaseGroup(ownGroup_);
// Finish and commit measures // Finish and commit measures
exit(); 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: private:
void enter() { void enter() {
JSRuntime* runtime = cx_->runtime(); JSRuntime* runtime = cx_->runtime();
if (runtime->stopwatch.isMonitoringCPOW()) { if (runtime->stopwatch.isMonitoringCPOW()) {
CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime; CPOWTimeStart_ = runtime->stopwatch.performance.getOwnGroup()->data.totalCPOWTime;
isMonitoringCPOW_ = true; isMonitoringCPOW_ = true;
} }
@ -563,46 +517,54 @@ class AutoStopwatch final
uint64_t CPOWTimeDelta = 0; uint64_t CPOWTimeDelta = 0;
if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) { if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) {
// We were monitoring CPOW when we entered and we still are. // 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); commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta);
} }
void commitDeltasToGroups(uint64_t userTimeDelta, // Attempt to acquire a group
uint64_t systemTimeDelta, // If the group is `null` or if the group already has a stopwatch,
uint64_t CPOWTimeDelta) // do nothing and return `null`.
{ // Otherwise, bind the group to `this` for the current iteration
JSCompartment* compartment = cx_->compartment(); // and return `group`.
PerformanceGroup* acquireGroup(PerformanceGroup* group) {
if (!group)
return nullptr;
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); if (group->hasStopwatch(iteration_))
MOZ_ASSERT(sharedGroup); return nullptr;
applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup->data);
if (isMonitoringForSelf_) { group->acquireStopwatch(iteration_, this);
PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_); return group;
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 applyDeltas(uint64_t userTimeDelta, void commitDeltasToGroups(uint64_t userTimeDelta, uint64_t systemTimeDelta,
uint64_t systemTimeDelta, uint64_t CPOWTimeDelta) const {
uint64_t CPOWTimeDelta, applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup_);
PerformanceData& data) const { 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; uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
data.totalUserTime += userTimeDelta; group->data.totalUserTime += userTimeDelta;
data.totalSystemTime += systemTimeDelta; group->data.totalSystemTime += systemTimeDelta;
data.totalCPOWTime += CPOWTimeDelta; group->data.totalCPOWTime += CPOWTimeDelta;
// Update an array containing the number of times we have missed // Update an array containing the number of times we have missed
// at least 2^0 successive ms, 2^1 successive ms, ... // at least 2^0 successive ms, 2^1 successive ms, ...
@ -612,10 +574,10 @@ class AutoStopwatch final
size_t i = 0; size_t i = 0;
uint64_t duration = 1000; uint64_t duration = 1000;
for (i = 0, duration = 1000; for (i = 0, duration = 1000;
i < ArrayLength(data.durations) && duration < totalTimeDelta; i < ArrayLength(group->data.durations) && duration < totalTimeDelta;
++i, duration *= 2) ++i, duration *= 2)
{ {
data.durations[i]++; group->data.durations[i]++;
} }
} }

View File

@ -222,7 +222,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
largeAllocationFailureCallback(nullptr), largeAllocationFailureCallback(nullptr),
oomCallback(nullptr), oomCallback(nullptr),
debuggerMallocSizeOf(ReturnZeroSize), debuggerMallocSizeOf(ReturnZeroSize),
lastAnimationTime(0) lastAnimationTime(0),
stopwatch(thisFromCtor())
{ {
setGCStoreBufferPtr(&gc.storeBuffer); setGCStoreBufferPtr(&gc.storeBuffer);
@ -930,40 +931,17 @@ js::PerformanceGroupHolder::getHashKey(JSContext* cx)
void void
js::PerformanceGroupHolder::unlink() js::PerformanceGroupHolder::unlink()
{ {
if (ownGroup_) { ownGroup_ = nullptr;
js_delete(ownGroup_);
ownGroup_ = nullptr;
}
if (!sharedGroup_) {
// The group has never been instantiated.
return;
}
js::PerformanceGroup* group = sharedGroup_;
sharedGroup_ = 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* PerformanceGroup*
js::PerformanceGroupHolder::getOwnGroup(JSContext* cx) js::PerformanceGroupHolder::getOwnGroup()
{ {
if (ownGroup_) if (ownGroup_)
return ownGroup_; return ownGroup_;
ownGroup_ = runtime_->new_<PerformanceGroup>(cx, nullptr); return ownGroup_ = runtime_->new_<PerformanceGroup>(runtime_);
return ownGroup_;
} }
PerformanceGroup* PerformanceGroup*
@ -985,31 +963,60 @@ js::PerformanceGroupHolder::getSharedGroup(JSContext* cx)
if (!sharedGroup_) if (!sharedGroup_)
return nullptr; return nullptr;
if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) { runtime_->stopwatch.groups().add(ptr, key, sharedGroup_);
js_delete(sharedGroup_);
sharedGroup_ = nullptr;
return nullptr;
}
} }
sharedGroup_->incRefCount();
return sharedGroup_; return sharedGroup_;
} }
PerformanceData* PerformanceData*
js::GetPerformanceData(JSRuntime* rt) js::GetPerformanceData(JSRuntime* rt)
{ {
return &rt->stopwatch.performance; return &rt->stopwatch.performance.getOwnGroup()->data;
} }
js::PerformanceGroup::PerformanceGroup(JSContext* cx, void* key) js::PerformanceGroup::PerformanceGroup(JSRuntime* rt)
: uid(cx->runtime()->stopwatch.uniqueId()) : uid(rt->stopwatch.uniqueId()),
, stopwatch_(nullptr) runtime_(rt),
, iteration_(0) stopwatch_(nullptr),
, key_(key) iteration_(0),
, refCount_(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 void

View File

@ -1512,6 +1512,11 @@ struct JSRuntime : public JS::shadow::Runtime,
return groups_; return groups_;
} }
/**
* Performance data on the entire runtime.
*/
js::PerformanceGroupHolder performance;
/** /**
* The number of times we have entered the event loop. * The number of times we have entered the event loop.
* Used to reset counters whenever we enter the loop, * Used to reset counters whenever we enter the loop,
@ -1523,17 +1528,6 @@ struct JSRuntime : public JS::shadow::Runtime,
*/ */
uint64_t iteration; 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 * Callback used to ask the embedding to determine in which
* Performance Group the current execution belongs. Typically, this is * Performance Group the current execution belongs. Typically, this is
@ -1546,9 +1540,9 @@ struct JSRuntime : public JS::shadow::Runtime,
*/ */
JSCurrentPerfGroupCallback currentPerfGroupCallback; JSCurrentPerfGroupCallback currentPerfGroupCallback;
Stopwatch() explicit Stopwatch(JSRuntime* runtime)
: iteration(0) : performance(runtime)
, isEmpty(true) , iteration(0)
, currentPerfGroupCallback(nullptr) , currentPerfGroupCallback(nullptr)
, isMonitoringJank_(false) , isMonitoringJank_(false)
, isMonitoringCPOW_(false) , isMonitoringCPOW_(false)
@ -1565,7 +1559,6 @@ struct JSRuntime : public JS::shadow::Runtime,
*/ */
void reset() { void reset() {
++iteration; ++iteration;
isEmpty = true;
} }
/** /**
* Activate/deactivate stopwatch measurement of jank. * Activate/deactivate stopwatch measurement of jank.
@ -1652,6 +1645,9 @@ struct JSRuntime : public JS::shadow::Runtime,
MonotonicTimeStamp userTimeFix; MonotonicTimeStamp userTimeFix;
private: private:
Stopwatch(const Stopwatch&) = delete;
Stopwatch& operator=(const Stopwatch&) = delete;
Groups groups_; Groups groups_;
friend struct js::PerformanceGroupHolder; friend struct js::PerformanceGroupHolder;