зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1147664 - Detailed mode for PerformanceStats (low-level). r=jandem
--HG-- extra : transplant_source : %BC%A9%AA%3C%1B%9A1%CCh%08%96%C0q%EB%E4%3C%9C%99%9B%C0
This commit is contained in:
Родитель
827f1cc53c
Коммит
7538ba910f
|
@ -317,16 +317,21 @@ IterPerformanceStats(JSContext* cx,
|
|||
}
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
|
||||
|
||||
// First report the shared groups
|
||||
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
|
||||
JSCompartment* compartment = c.get();
|
||||
if (!compartment->performanceMonitoring.isLinked()) {
|
||||
if (!c->principals()) {
|
||||
// Compartments without principals could show up here, but
|
||||
// reporting them doesn't really make sense.
|
||||
continue;
|
||||
}
|
||||
if (!c->performanceMonitoring.hasSharedGroup()) {
|
||||
// Don't report compartments that do not even have a PerformanceGroup.
|
||||
continue;
|
||||
}
|
||||
|
||||
js::AutoCompartment autoCompartment(cx, compartment);
|
||||
PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx);
|
||||
|
||||
PerformanceGroup* group = compartment->performanceMonitoring.getSharedGroup(cx);
|
||||
if (group->data.ticks == 0) {
|
||||
// Don't report compartments that have never been used.
|
||||
continue;
|
||||
|
@ -338,7 +343,9 @@ IterPerformanceStats(JSContext* cx,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!(*walker)(cx, group->data, group->uid, closure)) {
|
||||
if (!(*walker)(cx,
|
||||
group->data, group->uid, nullptr,
|
||||
closure)) {
|
||||
// Issue in callback
|
||||
return false;
|
||||
}
|
||||
|
@ -347,6 +354,35 @@ 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;
|
||||
}
|
||||
|
|
|
@ -5409,7 +5409,7 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp)
|
|||
|
||||
namespace js {
|
||||
|
||||
struct AutoStopwatch;
|
||||
class AutoStopwatch;
|
||||
|
||||
// Container for performance data
|
||||
// All values are monotonic.
|
||||
|
@ -5540,21 +5540,30 @@ private:
|
|||
};
|
||||
|
||||
//
|
||||
// Indirection towards a PerformanceGroup.
|
||||
// This structure handles reference counting for instances of PerformanceGroup.
|
||||
// Each PerformanceGroupHolder handles:
|
||||
// - a reference-counted indirection towards a PerformanceGroup shared
|
||||
// by several compartments
|
||||
// - a owned PerformanceGroup representing the performance of a single
|
||||
// compartment.
|
||||
//
|
||||
struct PerformanceGroupHolder {
|
||||
// Get the group.
|
||||
// Get the shared group.
|
||||
// On first call, this causes a single Hashtable lookup.
|
||||
// Successive calls do not require further lookups.
|
||||
js::PerformanceGroup* getGroup(JSContext*);
|
||||
js::PerformanceGroup* getSharedGroup(JSContext*);
|
||||
|
||||
// `true` if the this holder is currently associated to a
|
||||
// Get the own group.
|
||||
js::PerformanceGroup* getOwnGroup(JSContext*);
|
||||
|
||||
// `true` if the this holder is currently associated to a shared
|
||||
// PerformanceGroup, `false` otherwise. Use this method to avoid
|
||||
// instantiating a PerformanceGroup if you only need to get
|
||||
// available performance data.
|
||||
inline bool isLinked() const {
|
||||
return group_ != nullptr;
|
||||
inline bool hasSharedGroup() const {
|
||||
return sharedGroup_ != nullptr;
|
||||
}
|
||||
inline bool hasOwnGroup() const {
|
||||
return ownGroup_ != nullptr;
|
||||
}
|
||||
|
||||
// Remove the link to the PerformanceGroup. This method is designed
|
||||
|
@ -5564,10 +5573,12 @@ struct PerformanceGroupHolder {
|
|||
|
||||
explicit PerformanceGroupHolder(JSRuntime* runtime)
|
||||
: runtime_(runtime)
|
||||
, group_(nullptr)
|
||||
, sharedGroup_(nullptr)
|
||||
, ownGroup_(nullptr)
|
||||
{ }
|
||||
~PerformanceGroupHolder();
|
||||
private:
|
||||
|
||||
private:
|
||||
// Return the key representing this PerformanceGroup in
|
||||
// Runtime::Stopwatch.
|
||||
// Do not deallocate the key.
|
||||
|
@ -5575,10 +5586,11 @@ private:
|
|||
|
||||
JSRuntime *runtime_;
|
||||
|
||||
// The PerformanceGroup held by this object.
|
||||
// Initially set to `nullptr` until the first cal to `getGroup`.
|
||||
// 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* group_;
|
||||
js::PerformanceGroup* sharedGroup_;
|
||||
js::PerformanceGroup* ownGroup_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -5604,6 +5616,10 @@ 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*);
|
||||
|
@ -5615,7 +5631,9 @@ extern JS_PUBLIC_API(PerformanceData*)
|
|||
GetPerformanceData(JSRuntime*);
|
||||
|
||||
typedef bool
|
||||
(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, uint64_t uid, void* closure);
|
||||
(PerformanceStatsWalker)(JSContext* cx,
|
||||
const PerformanceData& stats, uint64_t uid,
|
||||
const uint64_t* parentId, void* closure);
|
||||
|
||||
/**
|
||||
* Extract the performance statistics.
|
||||
|
|
|
@ -372,15 +372,49 @@ 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).
|
||||
struct AutoStopwatch final
|
||||
class 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.
|
||||
|
@ -389,120 +423,198 @@ struct AutoStopwatch final
|
|||
explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: cx_(cx)
|
||||
, iteration_(0)
|
||||
, isActive_(false)
|
||||
, isTop_(false)
|
||||
, isMonitoringForGroup_(false)
|
||||
, isMonitoringForSelf_(false)
|
||||
, isMonitoringForTop_(false)
|
||||
, isMonitoringJank_(false)
|
||||
, isMonitoringCPOW_(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 *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.
|
||||
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;
|
||||
}
|
||||
|
||||
// Start the stopwatch.
|
||||
if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_))
|
||||
return;
|
||||
isActive_ = true;
|
||||
CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
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;
|
||||
runtime->stopwatch.performance.ticks++;
|
||||
isTop_ = true;
|
||||
isMonitoringForTop_ = true;
|
||||
|
||||
MOZ_ASSERT(isMonitoringForGroup_);
|
||||
}
|
||||
}
|
||||
inline ~AutoStopwatch() {
|
||||
if (!isActive_) {
|
||||
|
||||
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_) {
|
||||
// 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();
|
||||
|
||||
MOZ_ASSERT(!compartment->scheduledForDestruction);
|
||||
|
||||
if (!runtime->stopwatch.isMonitoringJank()) {
|
||||
// Monitoring has been stopped while we were
|
||||
// executing the code. Drop everything.
|
||||
if (compartment->scheduledForDestruction)
|
||||
return;
|
||||
}
|
||||
|
||||
JSRuntime* runtime = cx_->runtime();
|
||||
if (iteration_ != runtime->stopwatch.iteration) {
|
||||
// We have entered a nested event loop at some point.
|
||||
// Any information we may have is obsolete.
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx_);
|
||||
MOZ_ASSERT(group);
|
||||
// Finish and commit measures
|
||||
exit();
|
||||
|
||||
// Compute time spent.
|
||||
group->releaseStopwatch(iteration_, this);
|
||||
uint64_t userTimeEnd, systemTimeEnd;
|
||||
if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd))
|
||||
return;
|
||||
// Now release groups.
|
||||
if (isMonitoringForGroup_) {
|
||||
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_);
|
||||
MOZ_ASSERT(sharedGroup);
|
||||
sharedGroup->releaseStopwatch(iteration_, this);
|
||||
}
|
||||
|
||||
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;
|
||||
if (isMonitoringForSelf_) {
|
||||
PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_);
|
||||
MOZ_ASSERT(ownGroup);
|
||||
ownGroup->releaseStopwatch(iteration_, this);
|
||||
}
|
||||
|
||||
uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
|
||||
updateDurations(totalTimeDelta, group->data.durations);
|
||||
group->data.ticks++;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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<int N>
|
||||
void updateDurations(uint64_t totalTimeDelta, uint64_t (&array)[N]) const {
|
||||
void applyDeltas(uint64_t userTimeDelta,
|
||||
uint64_t systemTimeDelta,
|
||||
uint64_t CPOWTimeDelta,
|
||||
PerformanceData& data) const {
|
||||
|
||||
data.ticks++;
|
||||
|
||||
uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
|
||||
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, ...
|
||||
// 2^i successive ms.
|
||||
|
||||
// Duration of one frame, i.e. 16ms in museconds
|
||||
size_t i = 0;
|
||||
uint64_t duration = 1000;
|
||||
for (i = 0, duration = 1000;
|
||||
i < N && duration < totalTimeDelta;
|
||||
++i, duration *= 2) {
|
||||
array[i]++;
|
||||
i < ArrayLength(data.durations) && duration < totalTimeDelta;
|
||||
++i, duration *= 2)
|
||||
{
|
||||
data.durations[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -585,31 +697,9 @@ struct AutoStopwatch final
|
|||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// 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,
|
||||
// `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
|
||||
private:
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -899,6 +899,17 @@ 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();
|
||||
|
@ -918,13 +929,18 @@ js::PerformanceGroupHolder::getHashKey(JSContext* cx)
|
|||
void
|
||||
js::PerformanceGroupHolder::unlink()
|
||||
{
|
||||
if (!group_) {
|
||||
if (ownGroup_) {
|
||||
js_delete(ownGroup_);
|
||||
ownGroup_ = nullptr;
|
||||
}
|
||||
|
||||
if (!sharedGroup_) {
|
||||
// The group has never been instantiated.
|
||||
return;
|
||||
}
|
||||
|
||||
js::PerformanceGroup* group = group_;
|
||||
group_ = nullptr;
|
||||
js::PerformanceGroup* group = sharedGroup_;
|
||||
sharedGroup_ = nullptr;
|
||||
|
||||
if (group->decRefCount() > 0) {
|
||||
// The group has at least another owner.
|
||||
|
@ -933,32 +949,51 @@ 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::getGroup(JSContext* cx)
|
||||
js::PerformanceGroupHolder::getOwnGroup(JSContext* cx)
|
||||
{
|
||||
if (group_)
|
||||
return group_;
|
||||
if (ownGroup_)
|
||||
return ownGroup_;
|
||||
|
||||
ownGroup_ = runtime_->new_<PerformanceGroup>(cx, nullptr);
|
||||
return ownGroup_;
|
||||
}
|
||||
|
||||
PerformanceGroup*
|
||||
js::PerformanceGroupHolder::getSharedGroup(JSContext* cx)
|
||||
{
|
||||
if (sharedGroup_)
|
||||
return sharedGroup_;
|
||||
|
||||
if (!runtime_->stopwatch.groups().initialized())
|
||||
return nullptr;
|
||||
|
||||
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) {
|
||||
group_ = ptr->value();
|
||||
MOZ_ASSERT(group_);
|
||||
sharedGroup_ = ptr->value();
|
||||
MOZ_ASSERT(sharedGroup_);
|
||||
} else {
|
||||
group_ = runtime_->new_<PerformanceGroup>(cx, key);
|
||||
runtime_->stopwatch.groups_.add(ptr, key, group_);
|
||||
sharedGroup_ = runtime_->new_<PerformanceGroup>(cx, key);
|
||||
if (!sharedGroup_)
|
||||
return nullptr;
|
||||
|
||||
if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) {
|
||||
js_delete(sharedGroup_);
|
||||
sharedGroup_ = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
group_->incRefCount();
|
||||
sharedGroup_->incRefCount();
|
||||
|
||||
return group_;
|
||||
return sharedGroup_;
|
||||
}
|
||||
|
||||
PerformanceData*
|
||||
|
|
|
@ -1483,6 +1483,29 @@ 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<void*, js::PerformanceGroup*,
|
||||
js::DefaultHasher<void*>,
|
||||
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,
|
||||
|
@ -1523,6 +1546,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
, currentPerfGroupCallback(nullptr)
|
||||
, isMonitoringJank_(false)
|
||||
, isMonitoringCPOW_(false)
|
||||
, isMonitoringPerCompartment_(false)
|
||||
, idCounter_(0)
|
||||
{ }
|
||||
|
||||
|
@ -1564,6 +1588,21 @@ 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.
|
||||
|
@ -1607,25 +1646,6 @@ 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<void*, js::PerformanceGroup*,
|
||||
js::DefaultHasher<void*>,
|
||||
js::SystemAllocPolicy> Groups;
|
||||
|
||||
Groups groups_;
|
||||
friend struct js::PerformanceGroupHolder;
|
||||
|
||||
|
@ -1634,6 +1654,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
*/
|
||||
bool isMonitoringJank_;
|
||||
bool isMonitoringCPOW_;
|
||||
bool isMonitoringPerCompartment_;
|
||||
|
||||
/**
|
||||
* A counter used to generate unique identifiers for groups.
|
||||
|
|
|
@ -234,6 +234,24 @@ nsPerformanceSnapshot::GetWindowData(JSContext* cx,
|
|||
|
||||
doc->GetTitle(title);
|
||||
*windowId = ptop->WindowID();
|
||||
|
||||
*memoryUsed = -1;
|
||||
int64_t jsObjectsSize;
|
||||
int64_t jsStringsSize;
|
||||
int64_t jsOtherSize;
|
||||
int64_t domSize;
|
||||
int64_t styleSize;
|
||||
int64_t otherSize;
|
||||
double jsMilliseconds;
|
||||
double nonJSMilliseconds;
|
||||
nsCOMPtr<nsIMemoryReporterManager> manager = nsMemoryReporterManager::GetOrCreate();
|
||||
if (!manager) {
|
||||
return;
|
||||
}
|
||||
nsresult rv = manager->GetSizeOfTab(top, &jsObjectsSize, &jsStringsSize, &jsOtherSize, &domSize, &styleSize, memoryUsed);
|
||||
if (NS_FAILED(rv)) {
|
||||
*memoryUsed = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
|
Загрузка…
Ссылка в новой задаче