зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changesets da71c4aefb3a and 98603c32c9a3 (bug 1149486) for browser_compartments.js permafail.
CLOSED TREE --HG-- extra : amend_source : bb03b38dccd8a194407ae0c5f1f8fece0fc97dc3
This commit is contained in:
Родитель
2fd90bf639
Коммит
1dc9d316cf
|
@ -274,10 +274,9 @@ JS_GetEmptyString(JSRuntime* rt)
|
|||
namespace js {
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
IterPerformanceStats(JSContext* cx,
|
||||
PerformanceStatsWalker walker,
|
||||
PerformanceData* processStats,
|
||||
void* closure)
|
||||
GetPerformanceStats(JSRuntime* rt,
|
||||
PerformanceStatsVector& stats,
|
||||
PerformanceStats& processStats)
|
||||
{
|
||||
// As a PerformanceGroup is typically associated to several
|
||||
// compartments, use a HashSet to make sure that we only report
|
||||
|
@ -290,16 +289,13 @@ IterPerformanceStats(JSContext* cx,
|
|||
return false;
|
||||
}
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
|
||||
JSCompartment* compartment = c.get();
|
||||
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.getGroup(cx);
|
||||
PerformanceGroup* group = compartment->performanceMonitoring.getGroup();
|
||||
|
||||
if (group->data.ticks == 0) {
|
||||
// Don't report compartments that have never been used.
|
||||
|
@ -312,16 +308,38 @@ IterPerformanceStats(JSContext* cx,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!(*walker)(cx, group->data, closure)) {
|
||||
// Issue in callback
|
||||
if (!stats.growBy(1)) {
|
||||
// Memory issue
|
||||
return false;
|
||||
}
|
||||
PerformanceStats* stat = &stats.back();
|
||||
stat->isSystem = compartment->isSystem();
|
||||
if (compartment->addonId)
|
||||
stat->addonId = compartment->addonId;
|
||||
|
||||
if (compartment->addonId || !compartment->isSystem()) {
|
||||
if (rt->compartmentNameCallback) {
|
||||
(*rt->compartmentNameCallback)(rt, compartment,
|
||||
stat->name,
|
||||
mozilla::ArrayLength(stat->name));
|
||||
} else {
|
||||
strcpy(stat->name, "<unknown>");
|
||||
}
|
||||
} else {
|
||||
strcpy(stat->name, "<platform>");
|
||||
}
|
||||
stat->performance = group->data;
|
||||
if (!set.add(ptr, group)) {
|
||||
// Memory issue
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*processStats = rt->stopwatch.performance;
|
||||
|
||||
strcpy(processStats.name, "<process>");
|
||||
processStats.addonId = nullptr;
|
||||
processStats.isSystem = true;
|
||||
processStats.performance = rt->stopwatch.performance;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -911,7 +929,6 @@ JSAutoCompartment::JSAutoCompartment(JSContext* cx, JSScript* target
|
|||
cx_->enterCompartment(target->compartment());
|
||||
}
|
||||
|
||||
|
||||
JSAutoCompartment::~JSAutoCompartment()
|
||||
{
|
||||
cx_->leaveCompartment(oldCompartment_);
|
||||
|
|
|
@ -720,18 +720,6 @@ typedef void
|
|||
(* JSCompartmentNameCallback)(JSRuntime* rt, JSCompartment* compartment,
|
||||
char* buf, size_t bufsize);
|
||||
|
||||
/**
|
||||
* Callback used to ask the embedding to determine in which
|
||||
* Performance Group the current execution belongs. Typically, this is
|
||||
* used to regroup JSCompartments from several iframes from the same
|
||||
* page or from several compartments of the same addon into a single
|
||||
* Performance Group.
|
||||
*
|
||||
* Returns an opaque key.
|
||||
*/
|
||||
typedef void*
|
||||
(* JSCurrentPerfGroupCallback)(JSContext*);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static MOZ_ALWAYS_INLINE jsval
|
||||
|
@ -5458,10 +5446,9 @@ struct PerformanceGroup {
|
|||
stopwatch_ = nullptr;
|
||||
}
|
||||
|
||||
explicit PerformanceGroup(void* key)
|
||||
PerformanceGroup()
|
||||
: stopwatch_(nullptr)
|
||||
, iteration_(0)
|
||||
, key_(key)
|
||||
, refCount_(0)
|
||||
{ }
|
||||
~PerformanceGroup()
|
||||
|
@ -5480,9 +5467,6 @@ struct PerformanceGroup {
|
|||
// may safely overflow.
|
||||
uint64_t iteration_;
|
||||
|
||||
// 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);
|
||||
|
@ -5494,7 +5478,7 @@ struct PerformanceGroup {
|
|||
}
|
||||
friend struct PerformanceGroupHolder;
|
||||
|
||||
private:
|
||||
private:
|
||||
// A reference counter. Maintained by PerformanceGroupHolder.
|
||||
uint64_t refCount_;
|
||||
};
|
||||
|
@ -5507,7 +5491,7 @@ struct PerformanceGroupHolder {
|
|||
// Get the group.
|
||||
// On first call, this causes a single Hashtable lookup.
|
||||
// Successive calls do not require further lookups.
|
||||
js::PerformanceGroup* getGroup(JSContext*);
|
||||
js::PerformanceGroup* getGroup();
|
||||
|
||||
// `true` if the this holder is currently associated to a
|
||||
// PerformanceGroup, `false` otherwise. Use this method to avoid
|
||||
|
@ -5522,8 +5506,9 @@ struct PerformanceGroupHolder {
|
|||
// (new values of `isSystem()`, `principals()` or `addonId`).
|
||||
void unlink();
|
||||
|
||||
explicit PerformanceGroupHolder(JSRuntime* runtime)
|
||||
PerformanceGroupHolder(JSRuntime* runtime, JSCompartment* compartment)
|
||||
: runtime_(runtime)
|
||||
, compartment_(compartment)
|
||||
, group_(nullptr)
|
||||
{ }
|
||||
~PerformanceGroupHolder();
|
||||
|
@ -5531,9 +5516,10 @@ private:
|
|||
// Return the key representing this PerformanceGroup in
|
||||
// Runtime::Stopwatch.
|
||||
// Do not deallocate the key.
|
||||
void* getHashKey(JSContext* cx);
|
||||
void* getHashKey();
|
||||
|
||||
JSRuntime *runtime_;
|
||||
JSRuntime* runtime_;
|
||||
JSCompartment* compartment_;
|
||||
|
||||
// The PerformanceGroup held by this object.
|
||||
// Initially set to `nullptr` until the first cal to `getGroup`.
|
||||
|
@ -5566,30 +5552,56 @@ IsStopwatchActive(JSRuntime*);
|
|||
extern JS_PUBLIC_API(PerformanceData*)
|
||||
GetPerformanceData(JSRuntime*);
|
||||
|
||||
typedef bool
|
||||
(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, void* closure);
|
||||
|
||||
/**
|
||||
* Performance statistics for a performance group (a process, an
|
||||
* add-on, a webpage, the built-ins or a special compartment).
|
||||
*/
|
||||
struct PerformanceStats {
|
||||
/**
|
||||
* If this group represents an add-on, the ID of the addon,
|
||||
* otherwise `nullptr`.
|
||||
*/
|
||||
JSAddonId* addonId;
|
||||
|
||||
/**
|
||||
* If this group represents a webpage, the process itself or a special
|
||||
* compartment, a human-readable name. Unspecified for add-ons.
|
||||
*/
|
||||
char name[1024];
|
||||
|
||||
/**
|
||||
* `true` if the group represents in system compartments, `false`
|
||||
* otherwise. A group may never contain both system and non-system
|
||||
* compartments.
|
||||
*/
|
||||
bool isSystem;
|
||||
|
||||
/**
|
||||
* Performance information.
|
||||
*/
|
||||
js::PerformanceData performance;
|
||||
|
||||
PerformanceStats()
|
||||
: addonId(nullptr)
|
||||
, isSystem(false)
|
||||
{
|
||||
name[0] = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
typedef js::Vector<PerformanceStats, 0, js::SystemAllocPolicy> PerformanceStatsVector;
|
||||
|
||||
/**
|
||||
* Extract the performance statistics.
|
||||
*
|
||||
* Note that before calling `walker`, we enter the corresponding context.
|
||||
* After a successful call, `stats` holds the `PerformanceStats` for
|
||||
* all performance groups, and `global` holds a `PerformanceStats`
|
||||
* representing the entire process.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IterPerformanceStats(JSContext* cx, PerformanceStatsWalker* walker, js::PerformanceData* process, void* closure);
|
||||
GetPerformanceStats(JSRuntime* rt, js::PerformanceStatsVector& stats, js::PerformanceStats& global);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/**
|
||||
* Callback used to ask the embedding to determine in which
|
||||
* Performance Group a compartment belongs. Typically, this is used to
|
||||
* regroup JSCompartments from several iframes from the same page or
|
||||
* from several compartments of the same addon into a single
|
||||
* Performance Group.
|
||||
*
|
||||
* Returns an opaque key.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetCurrentPerfGroupCallback(JSRuntime *rt, JSCurrentPerfGroupCallback cb);
|
||||
|
||||
|
||||
#endif /* jsapi_h */
|
||||
|
|
|
@ -53,7 +53,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
|
|||
#endif
|
||||
global_(nullptr),
|
||||
enterCompartmentDepth(0),
|
||||
performanceMonitoring(runtime_),
|
||||
performanceMonitoring(runtime_, this),
|
||||
data(nullptr),
|
||||
objectMetadataCallback(nullptr),
|
||||
lastAnimationTime(0),
|
||||
|
|
|
@ -409,7 +409,8 @@ struct AutoStopwatch final
|
|||
//
|
||||
// Previous owner is restored upon destruction.
|
||||
explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: cx_(cx)
|
||||
: compartment_(nullptr)
|
||||
, runtime_(nullptr)
|
||||
, iteration_(0)
|
||||
, isActive_(false)
|
||||
, isTop_(false)
|
||||
|
@ -418,17 +419,16 @@ struct AutoStopwatch final
|
|||
, CPOWTimeStart_(0)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
|
||||
JSRuntime* runtime = JS_GetRuntime(cx_);
|
||||
if (!runtime->stopwatch.isActive())
|
||||
runtime_ = cx->runtime();
|
||||
if (!runtime_->stopwatch.isActive())
|
||||
return;
|
||||
|
||||
JSCompartment* compartment = cx_->compartment();
|
||||
if (compartment->scheduledForDestruction)
|
||||
compartment_ = cx->compartment();
|
||||
MOZ_ASSERT(compartment_);
|
||||
if (compartment_->scheduledForDestruction)
|
||||
return;
|
||||
iteration_ = runtime->stopwatch.iteration;
|
||||
iteration_ = runtime_->stopwatch.iteration;
|
||||
|
||||
PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx);
|
||||
PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
|
||||
MOZ_ASSERT(group);
|
||||
|
||||
if (group->hasStopwatch(iteration_)) {
|
||||
|
@ -438,22 +438,22 @@ struct AutoStopwatch final
|
|||
}
|
||||
|
||||
// Start the stopwatch.
|
||||
if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_))
|
||||
if (!this->getTimes(&userTimeStart_, &systemTimeStart_))
|
||||
return;
|
||||
isActive_ = true;
|
||||
CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime;
|
||||
CPOWTimeStart_ = runtime_->stopwatch.performance.totalCPOWTime;
|
||||
|
||||
// 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) {
|
||||
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++;
|
||||
runtime_->stopwatch.isEmpty = false;
|
||||
runtime_->stopwatch.performance.ticks++;
|
||||
isTop_ = true;
|
||||
}
|
||||
}
|
||||
|
@ -463,35 +463,32 @@ struct AutoStopwatch final
|
|||
return;
|
||||
}
|
||||
|
||||
JSRuntime* runtime = JS_GetRuntime(cx_);
|
||||
JSCompartment* compartment = cx_->compartment();
|
||||
MOZ_ASSERT(!compartment_->scheduledForDestruction);
|
||||
|
||||
MOZ_ASSERT(!compartment->scheduledForDestruction);
|
||||
|
||||
if (!runtime->stopwatch.isActive()) {
|
||||
if (!runtime_->stopwatch.isActive()) {
|
||||
// Monitoring has been stopped while we were
|
||||
// executing the code. Drop everything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (iteration_ != runtime->stopwatch.iteration) {
|
||||
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_);
|
||||
PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
|
||||
MOZ_ASSERT(group);
|
||||
|
||||
// Compute time spent.
|
||||
group->releaseStopwatch(iteration_, this);
|
||||
uint64_t userTimeEnd, systemTimeEnd;
|
||||
if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd))
|
||||
if (!this->getTimes(&userTimeEnd, &systemTimeEnd))
|
||||
return;
|
||||
|
||||
uint64_t userTimeDelta = userTimeEnd - userTimeStart_;
|
||||
uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_;
|
||||
uint64_t CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
|
||||
uint64_t CPOWTimeDelta = runtime_->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
|
||||
group->data.totalUserTime += userTimeDelta;
|
||||
group->data.totalSystemTime += systemTimeDelta;
|
||||
group->data.totalCPOWTime += CPOWTimeDelta;
|
||||
|
@ -503,10 +500,10 @@ struct AutoStopwatch final
|
|||
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;
|
||||
runtime_->stopwatch.performance.totalUserTime = userTimeEnd;
|
||||
runtime_->stopwatch.performance.totalSystemTime = systemTimeEnd;
|
||||
updateDurations(totalTimeDelta, runtime_->stopwatch.performance.durations);
|
||||
runtime_->stopwatch.isEmpty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,7 +527,7 @@ struct AutoStopwatch final
|
|||
// Get the OS-reported time spent in userland/systemland, in
|
||||
// microseconds. On most platforms, this data is per-thread,
|
||||
// but on some platforms we need to fall back to per-process.
|
||||
bool getTimes(JSRuntime* runtime, uint64_t* userTime, uint64_t* systemTime) const {
|
||||
bool getTimes(uint64_t* userTime, uint64_t* systemTime) const {
|
||||
MOZ_ASSERT(userTime);
|
||||
MOZ_ASSERT(systemTime);
|
||||
|
||||
|
@ -594,12 +591,12 @@ struct AutoStopwatch final
|
|||
kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
|
||||
kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
|
||||
// Convert 100 ns to 1 us, make sure that the result is monotonic
|
||||
*systemTime = runtime->stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10);
|
||||
*systemTime = runtime_-> stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10);
|
||||
|
||||
userTimeInt.LowPart = userFileTime.dwLowDateTime;
|
||||
userTimeInt.HighPart = userFileTime.dwHighDateTime;
|
||||
// Convert 100 ns to 1 us, make sure that the result is monotonic
|
||||
*userTime = runtime->stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
|
||||
*userTime = runtime_-> stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
|
||||
|
||||
#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
|
||||
|
||||
|
@ -607,9 +604,13 @@ struct AutoStopwatch final
|
|||
}
|
||||
|
||||
private:
|
||||
// The context with which this object was initialized.
|
||||
// The compartment with which this object was initialized.
|
||||
// Non-null.
|
||||
JSContext* const cx_;
|
||||
JSCompartment* compartment_;
|
||||
|
||||
// The runtime with which this object was initialized.
|
||||
// Non-null.
|
||||
JSRuntime* runtime_;
|
||||
|
||||
// An indication of the number of times we have entered the event
|
||||
// loop. Used only for comparison.
|
||||
|
|
|
@ -898,14 +898,15 @@ js::PerformanceGroupHolder::~PerformanceGroupHolder()
|
|||
}
|
||||
|
||||
void*
|
||||
js::PerformanceGroupHolder::getHashKey(JSContext* cx)
|
||||
js::PerformanceGroupHolder::getHashKey()
|
||||
{
|
||||
if (runtime_->stopwatch.currentPerfGroupCallback) {
|
||||
return (*runtime_->stopwatch.currentPerfGroupCallback)(cx);
|
||||
}
|
||||
|
||||
// As a fallback, put everything in the same PerformanceGroup.
|
||||
return nullptr;
|
||||
return compartment_->isSystem() ?
|
||||
(void*)compartment_->addonId :
|
||||
(void*)JS_GetCompartmentPrincipals(compartment_);
|
||||
// This key may be `nullptr` if we have `isSystem() == true`
|
||||
// and `compartment_->addonId`. This is absolutely correct,
|
||||
// and this represents the `PerformanceGroup` used to track
|
||||
// the performance of the the platform compartments.
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -926,26 +927,26 @@ js::PerformanceGroupHolder::unlink()
|
|||
|
||||
|
||||
JSRuntime::Stopwatch::Groups::Ptr ptr =
|
||||
runtime_->stopwatch.groups_.lookup(group->key_);
|
||||
runtime_->stopwatch.groups_.lookup(getHashKey());
|
||||
MOZ_ASSERT(ptr);
|
||||
runtime_->stopwatch.groups_.remove(ptr);
|
||||
js_delete(group);
|
||||
}
|
||||
|
||||
PerformanceGroup*
|
||||
js::PerformanceGroupHolder::getGroup(JSContext* cx)
|
||||
js::PerformanceGroupHolder::getGroup()
|
||||
{
|
||||
if (group_)
|
||||
return group_;
|
||||
|
||||
void* key = getHashKey(cx);
|
||||
void* key = getHashKey();
|
||||
JSRuntime::Stopwatch::Groups::AddPtr ptr =
|
||||
runtime_->stopwatch.groups_.lookupForAdd(key);
|
||||
if (ptr) {
|
||||
group_ = ptr->value();
|
||||
MOZ_ASSERT(group_);
|
||||
} else {
|
||||
group_ = runtime_->new_<PerformanceGroup>(key);
|
||||
group_ = runtime_->new_<PerformanceGroup>();
|
||||
runtime_->stopwatch.groups_.add(ptr, key, group_);
|
||||
}
|
||||
|
||||
|
@ -959,9 +960,3 @@ js::GetPerformanceData(JSRuntime* rt)
|
|||
{
|
||||
return &rt->stopwatch.performance;
|
||||
}
|
||||
|
||||
void
|
||||
JS_SetCurrentPerfGroupCallback(JSRuntime *rt, JSCurrentPerfGroupCallback cb)
|
||||
{
|
||||
rt->stopwatch.currentPerfGroupCallback = cb;
|
||||
}
|
||||
|
|
|
@ -1492,22 +1492,9 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
*/
|
||||
js::PerformanceData performance;
|
||||
|
||||
/**
|
||||
* Callback used to ask the embedding to determine in which
|
||||
* Performance Group the current execution belongs. Typically, this is
|
||||
* used to regroup JSCompartments from several iframes from the same
|
||||
* page or from several compartments of the same addon into a single
|
||||
* Performance Group.
|
||||
*
|
||||
* May be `nullptr`, in which case we put all the JSCompartments
|
||||
* in the same PerformanceGroup.
|
||||
*/
|
||||
JSCurrentPerfGroupCallback currentPerfGroupCallback;
|
||||
|
||||
Stopwatch()
|
||||
: iteration(0)
|
||||
, isEmpty(true)
|
||||
, currentPerfGroupCallback(nullptr)
|
||||
, isActive_(false)
|
||||
{ }
|
||||
|
||||
|
|
|
@ -1779,20 +1779,6 @@ GetCompartmentName(JSCompartment* c, nsCString& name, int* anonymizeID,
|
|||
}
|
||||
}
|
||||
|
||||
extern void
|
||||
xpc::GetCurrentCompartmentName(JSContext* cx, nsCString& name)
|
||||
{
|
||||
RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
if (!global) {
|
||||
name.AssignLiteral("no global");
|
||||
return;
|
||||
}
|
||||
|
||||
JSCompartment* compartment = GetObjectCompartment(global);
|
||||
int anonymizeID = 0;
|
||||
GetCompartmentName(compartment, name, &anonymizeID, false);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
JSMainRuntimeGCHeapDistinguishedAmount()
|
||||
{
|
||||
|
@ -3316,47 +3302,6 @@ static const JSWrapObjectCallbacks WrapObjectCallbacks = {
|
|||
xpc::WrapperFactory::PrepareForWrapping
|
||||
};
|
||||
|
||||
/**
|
||||
* Group JSCompartments into PerformanceGroups.
|
||||
*
|
||||
* - All JSCompartments from the same add-on belong to the same
|
||||
* PerformanceGroup.
|
||||
* - All JSCompartments from the same same webpage (including
|
||||
* frames) belong to the same PerformanceGroup.
|
||||
* - All other JSCompartments (normally, system add-ons)
|
||||
* belong to to a big uncategorized PerformanceGroup.
|
||||
*/
|
||||
static void*
|
||||
GetCurrentPerfGroupCallback(JSContext* cx) {
|
||||
RootedObject global(cx, CurrentGlobalOrNull(cx));
|
||||
if (!global) {
|
||||
// This can happen for the atom compartments, which is system
|
||||
// code.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSAddonId* addonId = AddonIdOfObject(global);
|
||||
if (addonId) {
|
||||
// If this is an add-on, use the id as key.
|
||||
return addonId;
|
||||
}
|
||||
|
||||
// If the compartment belongs to a webpage, use the address of the
|
||||
// topmost scriptable window, hence regrouping all frames of a
|
||||
// window.
|
||||
nsRefPtr<nsGlobalWindow> win = WindowOrNull(global);
|
||||
if (win) {
|
||||
nsCOMPtr<nsIDOMWindow> top;
|
||||
nsresult rv = win->GetScriptableTop(getter_AddRefs(top));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
return top.get();
|
||||
}
|
||||
|
||||
// Otherwise, this is platform code, use `nullptr` as key.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
: CycleCollectedJSRuntime(nullptr, JS::DefaultHeapMaxBytes, JS::DefaultNurseryBytes),
|
||||
mJSContextStack(new XPCJSContextStack(this)),
|
||||
|
@ -3536,8 +3481,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
// Watch for the JS boolean options.
|
||||
ReloadPrefsCallback(nullptr, this);
|
||||
Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
|
||||
|
||||
JS_SetCurrentPerfGroupCallback(runtime, ::GetCurrentPerfGroupCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -139,6 +139,9 @@ XrayAwareCalleeGlobal(JSObject* fun);
|
|||
void
|
||||
TraceXPCGlobal(JSTracer* trc, JSObject* obj);
|
||||
|
||||
uint64_t
|
||||
GetCompartmentCPOWMicroseconds(JSCompartment* compartment);
|
||||
|
||||
} /* namespace xpc */
|
||||
|
||||
namespace JS {
|
||||
|
@ -529,12 +532,6 @@ void
|
|||
DispatchScriptErrorEvent(nsPIDOMWindow* win, JSRuntime* rt, xpc::ErrorReport* xpcReport,
|
||||
JS::Handle<JS::Value> exception);
|
||||
|
||||
// Return a name for the compartment.
|
||||
// This function makes reasonable efforts to make this name both mostly human-readable
|
||||
// and unique. However, there are no guarantees of either property.
|
||||
extern void
|
||||
GetCurrentCompartmentName(JSContext*, nsCString& name);
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
namespace mozilla {
|
||||
|
|
|
@ -11,9 +11,6 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|||
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
const { AddonWatcher } = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
|
||||
const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {});
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
const UPDATE_TOPIC = "about:performance-update-immediately";
|
||||
|
||||
/**
|
||||
* The various measures we display.
|
||||
|
@ -280,7 +277,7 @@ function updateLiveData() {
|
|||
_el.textContent = a ? a.name : _item.name
|
||||
});
|
||||
} else {
|
||||
el.textContent = item.title || item.name;
|
||||
el.textContent = item.name;
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
|
@ -297,8 +294,5 @@ function go() {
|
|||
document.getElementById("intervalDropdown").addEventListener("change", () => AutoUpdate.updateRefreshRate());
|
||||
|
||||
State.update();
|
||||
let observer = update;
|
||||
|
||||
Services.obs.addObserver(update, UPDATE_TOPIC, false);
|
||||
window.addEventListener("unload", () => Services.obs.removeObserver(update, UPDATE_TOPIC));
|
||||
}
|
||||
setTimeout(update, 1000);
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
[DEFAULT]
|
||||
head = head.js
|
||||
support-files =
|
||||
browser_compartments.html
|
||||
browser_compartments_frame.html
|
||||
browser_compartments_script.js
|
||||
|
||||
[browser_aboutperformance.js]
|
||||
skip-if = e10s # Feature not implemented yet – bug 1140310
|
||||
|
|
|
@ -11,19 +11,9 @@ const URL = "http://example.com/browser/toolkit/components/aboutperformance/test
|
|||
function frameScript() {
|
||||
"use strict";
|
||||
|
||||
addMessageListener("aboutperformance-test:done", () => {
|
||||
content.postMessage("stop", "*");
|
||||
sendAsyncMessage("aboutperformance-test:done", null);
|
||||
});
|
||||
addMessageListener("aboutperformance-test:setTitle", ({data: title}) => {
|
||||
content.document.title = title;
|
||||
sendAsyncMessage("aboutperformance-test:setTitle", null);
|
||||
});
|
||||
|
||||
addMessageListener("aboutperformance-test:hasItems", ({data: title}) => {
|
||||
Services.obs.notifyObservers(null, "about:performance-update-immediately", "");
|
||||
addMessageListener("aboutperformance-test:hasItems", ({data: url}) => {
|
||||
let hasPlatform = false;
|
||||
let hasTitle = false;
|
||||
let hasURL = false;
|
||||
|
||||
try {
|
||||
let eltData = content.document.getElementById("liveData");
|
||||
|
@ -34,51 +24,42 @@ function frameScript() {
|
|||
// Find if we have a row for "platform"
|
||||
hasPlatform = eltData.querySelector("tr.platform") != null;
|
||||
|
||||
// Find if we have a row for our content page
|
||||
let titles = [for (eltContent of eltData.querySelectorAll("td.contents.name")) eltContent.textContent];
|
||||
// Find if we have a row for our URL
|
||||
hasURL = false;
|
||||
for (let eltContent of eltData.querySelectorAll("tr.content td.name")) {
|
||||
if (eltContent.textContent == url) {
|
||||
hasURL = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hasTitle = titles.includes(title);
|
||||
} catch (ex) {
|
||||
Cu.reportError("Error in content: " + ex);
|
||||
Cu.reportError(ex.stack);
|
||||
} finally {
|
||||
sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasTitle});
|
||||
sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasURL});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
add_task(function* go() {
|
||||
info("Setting up about:performance");
|
||||
let tabAboutPerformance = gBrowser.selectedTab = gBrowser.addTab("about:performance");
|
||||
add_task(function* test() {
|
||||
let tabAboutPerformance = gBrowser.addTab("about:performance");
|
||||
let tabContent = gBrowser.addTab(URL);
|
||||
|
||||
yield ContentTask.spawn(tabAboutPerformance.linkedBrowser, null, frameScript);
|
||||
|
||||
info(`Setting up ${URL}`);
|
||||
let tabContent = gBrowser.addTab(URL);
|
||||
yield ContentTask.spawn(tabContent.linkedBrowser, null, frameScript);
|
||||
|
||||
let title = "Testing about:performance " + Math.random();
|
||||
info(`Setting up title ${title}`);
|
||||
while (true) {
|
||||
yield promiseContentResponse(tabContent.linkedBrowser, "aboutperformance-test:setTitle", title);
|
||||
yield new Promise(resolve => setTimeout(resolve, 100));
|
||||
let {hasPlatform, hasTitle} = (yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", title));
|
||||
info(`Platform: ${hasPlatform}, title: ${hasTitle}`);
|
||||
if (hasPlatform && hasTitle) {
|
||||
Assert.ok(true, "Found a row for <platform> and a row for our page");
|
||||
let {hasPlatform, hasURL} = (yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", URL));
|
||||
info(`Platform: ${hasPlatform}, url: ${hasURL}`);
|
||||
if (hasPlatform && hasURL) {
|
||||
Assert.ok(true, "Found a row for <platform> and a row for our URL");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
info("Cleaning up");
|
||||
yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:done", null);
|
||||
|
||||
info("Closing tabs");
|
||||
for (let tab of gBrowser.tabs) {
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
info("Done");
|
||||
gBrowser.selectedTab = null;
|
||||
gBrowser.removeTab(tabContent);
|
||||
gBrowser.removeTab(tabAboutPerformance);
|
||||
});
|
||||
|
|
|
@ -2,19 +2,24 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Main frame for test browser_aboutperformance.js
|
||||
browser_compartments.html
|
||||
</title>
|
||||
<script type="text/javascript">
|
||||
// Use some CPU.
|
||||
window.setInterval(() => {
|
||||
// Compute an arbitrary value, print it out to make sure that the JS
|
||||
// engine doesn't discard all our computation.
|
||||
var date = Date.now();
|
||||
var array = [];
|
||||
var i = 0;
|
||||
while (Date.now() - date <= 100) {
|
||||
array[i%2] = i++;
|
||||
}
|
||||
console.log("Arbitrary value", array);
|
||||
}, 300);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Main frame.
|
||||
|
||||
<iframe src="browser_compartments_frame.html?frame=1">
|
||||
Subframe 1
|
||||
</iframe>
|
||||
|
||||
<iframe src="browser_compartments_frame.html?frame=2">
|
||||
Subframe 2.
|
||||
</iframe>
|
||||
|
||||
browser_compartments.html
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Subframe for test browser_compartments.html (do not change this title)
|
||||
</title>
|
||||
<script src="browser_compartments_script.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
Subframe loaded.
|
||||
</body>
|
||||
</html>
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
var carryOn = true;
|
||||
|
||||
window.addEventListener("message", e => {
|
||||
console.log("frame content", "message", e);
|
||||
if ("title" in e.data) {
|
||||
document.title = e.data.title;
|
||||
}
|
||||
if ("stop" in e.data) {
|
||||
carryOn = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Use some CPU.
|
||||
var interval = window.setInterval(() => {
|
||||
if (!carryOn) {
|
||||
window.clearInterval(interval);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute an arbitrary value, print it out to make sure that the JS
|
||||
// engine doesn't discard all our computation.
|
||||
var date = Date.now();
|
||||
var array = [];
|
||||
var i = 0;
|
||||
while (Date.now() - date <= 100) {
|
||||
array[i%2] = i++;
|
||||
}
|
||||
}, 300);
|
|
@ -25,8 +25,7 @@ let performanceStatsService =
|
|||
|
||||
|
||||
const PROPERTIES_NUMBERED = ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"];
|
||||
const PROPERTIES_META_IMMUTABLE = ["name", "addonId", "isSystem"];
|
||||
const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title"];
|
||||
const PROPERTIES_META = ["name", "addonId", "isSystem"];
|
||||
const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META];
|
||||
|
||||
/**
|
||||
|
@ -42,15 +41,6 @@ const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META];
|
|||
*
|
||||
* @field {string} addonId The identifier of the addon (e.g. "myaddon@foo.bar").
|
||||
*
|
||||
* @field {string|null} title The title of the webpage to which this code
|
||||
* belongs. Note that this is the title of the entire webpage (i.e. the tab),
|
||||
* even if the code is executed in an iframe. Also note that this title may
|
||||
* change over time.
|
||||
*
|
||||
* @field {number} windowId The outer window ID of the top-level nsIDOMWindow
|
||||
* to which this code belongs. May be 0 if the code doesn't belong to any
|
||||
* nsIDOMWindow.
|
||||
*
|
||||
* @field {boolean} isSystem `true` if the component is a system component (i.e.
|
||||
* an add-on or platform-code), `false` otherwise (i.e. a webpage).
|
||||
*
|
||||
|
|
|
@ -28,8 +28,4 @@ EXPORTS += [
|
|||
'nsPerformanceStats.h'
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIArray.idl"
|
||||
#include "nsIDOMWindow.idl"
|
||||
|
||||
/**
|
||||
* Mechanisms for querying the current process about performance
|
||||
|
@ -22,7 +21,7 @@
|
|||
* All values are monotonic and are updated only when
|
||||
* `nsIPerformanceStatsService.isStopwatchActive` is `true`.
|
||||
*/
|
||||
[scriptable, uuid(7741ba2d-3171-42cd-9f36-e46a5ce6d5b1)]
|
||||
[scriptable, uuid(f015cbad-e16f-4982-a080-67e4e69a5b2e)]
|
||||
interface nsIPerformanceStats: nsISupports {
|
||||
/**
|
||||
* The name of the component:
|
||||
|
@ -39,18 +38,6 @@ interface nsIPerformanceStats: nsISupports {
|
|||
*/
|
||||
readonly attribute AString addonId;
|
||||
|
||||
/**
|
||||
* If the component is code executed in a window, the ID of the topmost
|
||||
* outer window (i.e. the tab), otherwise 0.
|
||||
*/
|
||||
readonly attribute uint64_t windowId;
|
||||
|
||||
/**
|
||||
* If the component is code executed in a window, the title of the topmost
|
||||
* window (i.e. the tab), otherwise an empty string.
|
||||
*/
|
||||
readonly attribute AString title;
|
||||
|
||||
/**
|
||||
* Total amount of time spent executing code in this group, in
|
||||
* microseconds.
|
||||
|
@ -104,7 +91,7 @@ interface nsIPerformanceSnapshot: nsISupports {
|
|||
nsIPerformanceStats getProcessData();
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(c989c220-6581-4252-9aad-358fdec9ec8c)]
|
||||
[scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)]
|
||||
interface nsIPerformanceStatsService : nsISupports {
|
||||
/**
|
||||
* `true` if we should monitor performance, `false` otherwise.
|
||||
|
@ -114,7 +101,7 @@ interface nsIPerformanceStatsService : nsISupports {
|
|||
/**
|
||||
* Capture a snapshot of the performance data.
|
||||
*/
|
||||
[implicit_jscontext] nsIPerformanceSnapshot getSnapshot();
|
||||
nsIPerformanceSnapshot getSnapshot();
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -3,37 +3,24 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsPerformanceStats.h"
|
||||
|
||||
#include "nsMemory.h"
|
||||
#include "nsLiteralString.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "jspubtd.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
class nsPerformanceStats: public nsIPerformanceStats {
|
||||
public:
|
||||
nsPerformanceStats(const nsAString& aName,
|
||||
const nsAString& aAddonId,
|
||||
const nsAString& aTitle,
|
||||
const uint64_t aWindowId,
|
||||
const bool aIsSystem,
|
||||
const js::PerformanceData& aPerformanceData)
|
||||
nsPerformanceStats(nsAString& aName, nsAString& aAddonId, bool aIsSystem, js::PerformanceData& aPerformanceData)
|
||||
: mName(aName)
|
||||
, mAddonId(aAddonId)
|
||||
, mTitle(aTitle)
|
||||
, mWindowId(aWindowId)
|
||||
, mIsSystem(aIsSystem)
|
||||
, mPerformanceData(aPerformanceData)
|
||||
{
|
||||
|
@ -54,24 +41,6 @@ public:
|
|||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute uint64_t windowId; */
|
||||
NS_IMETHOD GetWindowId(uint64_t *aWindowId) override {
|
||||
*aWindowId = mWindowId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute AString title; */
|
||||
NS_IMETHOD GetTitle(nsAString & aTitle) override {
|
||||
aTitle.Assign(mTitle);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute bool isSystem; */
|
||||
NS_IMETHOD GetIsSystem(bool *_retval) override {
|
||||
*_retval = mIsSystem;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute unsigned long long totalUserTime; */
|
||||
NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) override {
|
||||
*aTotalUserTime = mPerformanceData.totalUserTime;
|
||||
|
@ -109,13 +78,16 @@ public:
|
|||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute bool isSystem; */
|
||||
NS_IMETHOD GetIsSystem(bool *_retval) override {
|
||||
*_retval = mIsSystem;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mName;
|
||||
nsString mAddonId;
|
||||
nsString mTitle;
|
||||
uint64_t mWindowId;
|
||||
bool mIsSystem;
|
||||
|
||||
js::PerformanceData mPerformanceData;
|
||||
|
||||
virtual ~nsPerformanceStats() {}
|
||||
|
@ -131,45 +103,15 @@ public:
|
|||
NS_DECL_NSIPERFORMANCESNAPSHOT
|
||||
|
||||
nsPerformanceSnapshot();
|
||||
nsresult Init(JSContext*);
|
||||
nsresult Init();
|
||||
private:
|
||||
virtual ~nsPerformanceSnapshot();
|
||||
|
||||
/**
|
||||
* Import a `js::PerformanceStats` as a `nsIPerformanceStats`.
|
||||
*
|
||||
* Precondition: this method assumes that we have entered the JSCompartment for which data `c`
|
||||
* has been collected.
|
||||
*
|
||||
* `cx` may be `nullptr` if we are importing the statistics for the
|
||||
* entire process, rather than the statistics for a specific set of
|
||||
* compartments.
|
||||
* Import a `PerformanceStats` as a `nsIPerformanceStats`.
|
||||
*/
|
||||
already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data);
|
||||
already_AddRefed<nsIPerformanceStats> ImportStats(js::PerformanceStats* c);
|
||||
|
||||
/**
|
||||
* Callbacks for iterating through the `PerformanceStats` of a runtime.
|
||||
*/
|
||||
bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats);
|
||||
static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self);
|
||||
|
||||
// If the context represents a window, extract the title and window ID.
|
||||
// Otherwise, extract "" and 0.
|
||||
static void GetWindowData(JSContext*,
|
||||
nsString& title,
|
||||
uint64_t* windowId);
|
||||
|
||||
// If the context presents an add-on, extract the addon ID.
|
||||
// Otherwise, extract "".
|
||||
static void GetAddonId(JSContext*,
|
||||
JS::Handle<JSObject*> global,
|
||||
nsAString& addonId);
|
||||
|
||||
// Determine whether a context is part of the system principals.
|
||||
static bool GetIsSystem(JSContext*,
|
||||
JS::Handle<JSObject*> global);
|
||||
|
||||
private:
|
||||
nsCOMArray<nsIPerformanceStats> mComponentsData;
|
||||
nsCOMPtr<nsIPerformanceStats> mProcessData;
|
||||
};
|
||||
|
@ -184,119 +126,36 @@ nsPerformanceSnapshot::~nsPerformanceSnapshot()
|
|||
{
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsPerformanceSnapshot::GetWindowData(JSContext* cx,
|
||||
nsString& title,
|
||||
uint64_t* windowId)
|
||||
{
|
||||
MOZ_ASSERT(windowId);
|
||||
|
||||
title.SetIsVoid(true);
|
||||
*windowId = 0;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = xpc::CurrentWindowOrNull(cx);
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> top;
|
||||
nsresult rv = win->GetTop(getter_AddRefs(top));
|
||||
if (!top || NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> ptop = do_QueryInterface(top);
|
||||
if (!ptop) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = ptop->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
doc->GetTitle(title);
|
||||
*windowId = ptop->WindowID();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsPerformanceSnapshot::GetAddonId(JSContext*,
|
||||
JS::Handle<JSObject*> global,
|
||||
nsAString& addonId)
|
||||
{
|
||||
addonId.AssignLiteral("");
|
||||
|
||||
JSAddonId* jsid = AddonIdOfObject(global);
|
||||
if (!jsid) {
|
||||
return;
|
||||
}
|
||||
AssignJSFlatString(addonId, (JSFlatString*)jsid);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsPerformanceSnapshot::GetIsSystem(JSContext*,
|
||||
JS::Handle<JSObject*> global)
|
||||
{
|
||||
return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPerformanceStats>
|
||||
nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance) {
|
||||
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
|
||||
if (!global) {
|
||||
// While it is possible for a compartment to have no global
|
||||
// (e.g. atoms), this compartment is not very interesting for us.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsPerformanceSnapshot::ImportStats(js::PerformanceStats* c) {
|
||||
nsString addonId;
|
||||
GetAddonId(cx, global, addonId);
|
||||
|
||||
nsString title;
|
||||
uint64_t windowId;
|
||||
GetWindowData(cx, title, &windowId);
|
||||
|
||||
nsAutoString name;
|
||||
nsAutoCString cname;
|
||||
xpc::GetCurrentCompartmentName(cx, cname);
|
||||
name.Assign(NS_ConvertUTF8toUTF16(cname));
|
||||
|
||||
bool isSystem = GetIsSystem(cx, global);
|
||||
|
||||
nsCOMPtr<nsIPerformanceStats> result =
|
||||
new nsPerformanceStats(name, addonId, title, windowId, isSystem, performance);
|
||||
if (c->addonId) {
|
||||
AssignJSFlatString(addonId, (JSFlatString*)c->addonId);
|
||||
}
|
||||
nsCString cname(c->name);
|
||||
NS_ConvertUTF8toUTF16 name(cname);
|
||||
nsCOMPtr<nsIPerformanceStats> result = new nsPerformanceStats(name, addonId, c->isSystem, c->performance);
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self) {
|
||||
return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats);
|
||||
}
|
||||
|
||||
bool
|
||||
nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats) {
|
||||
nsCOMPtr<nsIPerformanceStats> result = ImportStats(cx, stats);
|
||||
if (result) {
|
||||
mComponentsData.AppendElement(result);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPerformanceSnapshot::Init(JSContext* cx) {
|
||||
js::PerformanceData processStats;
|
||||
if (!js::IterPerformanceStats(cx, nsPerformanceSnapshot::IterPerformanceStatsCallback, &processStats, this)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
nsPerformanceSnapshot::Init() {
|
||||
JSRuntime* rt;
|
||||
nsCOMPtr<nsIJSRuntimeService> svc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
|
||||
NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
|
||||
svc->GetRuntime(&rt);
|
||||
js::PerformanceStats processStats;
|
||||
js::PerformanceStatsVector componentsStats;
|
||||
if (!js::GetPerformanceStats(rt, componentsStats, processStats)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mProcessData = new nsPerformanceStats(NS_LITERAL_STRING("<process>"), // name
|
||||
NS_LITERAL_STRING(""), // add-on id
|
||||
NS_LITERAL_STRING(""), // title
|
||||
0, // window id
|
||||
true, // isSystem
|
||||
processStats);
|
||||
size_t num = componentsStats.length();
|
||||
for (size_t pos = 0; pos < num; pos++) {
|
||||
nsCOMPtr<nsIPerformanceStats> stats = ImportStats(&componentsStats[pos]);
|
||||
mComponentsData.AppendObject(stats);
|
||||
}
|
||||
mProcessData = ImportStats(&processStats);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -350,10 +209,10 @@ NS_IMETHODIMP nsPerformanceStatsService::SetIsStopwatchActive(JSContext* cx, boo
|
|||
}
|
||||
|
||||
/* readonly attribute nsIPerformanceSnapshot snapshot; */
|
||||
NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
|
||||
NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(nsIPerformanceSnapshot * *aSnapshot)
|
||||
{
|
||||
nsRefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
|
||||
nsresult rv = snapshot->Init(cx);
|
||||
nsresult rv = snapshot->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ head = head.js
|
|||
support-files =
|
||||
browser_Addons_sample.xpi
|
||||
browser_compartments.html
|
||||
browser_compartments_frame.html
|
||||
|
||||
[browser_AddonWatcher.js]
|
||||
[browser_compartments.js]
|
||||
|
|
|
@ -2,19 +2,24 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Main frame for test browser_compartments.js
|
||||
browser_compartments.html
|
||||
</title>
|
||||
<script type="text/javascript">
|
||||
// Use some CPU.
|
||||
window.setInterval(() => {
|
||||
// Compute an arbitrary value, print it out to make sure that the JS
|
||||
// engine doesn't discard all our computation.
|
||||
var date = Date.now();
|
||||
var array = [];
|
||||
var i = 0;
|
||||
while (Date.now() - date <= 100) {
|
||||
array[i%2] = i++;
|
||||
}
|
||||
console.log("Arbitrary value", array);
|
||||
}, 300);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Main frame.
|
||||
|
||||
<iframe src="browser_compartments_frame.html?frame=1">
|
||||
Subframe 1
|
||||
</iframe>
|
||||
|
||||
<iframe src="browser_compartments_frame.html?frame=2">
|
||||
Subframe 2.
|
||||
</iframe>
|
||||
|
||||
browser_compartments.html
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,17 +3,10 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that we see jank that takes place in a webpage,
|
||||
* and that jank from several iframes are actually charged
|
||||
* to the top window.
|
||||
*/
|
||||
Cu.import("resource://gre/modules/PerformanceStats.jsm", this);
|
||||
Cu.import("resource://testing-common/ContentTask.jsm", this);
|
||||
|
||||
const URL = "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random();
|
||||
const PARENT_TITLE = `Main frame for test browser_compartments.js ${Math.random()}`;
|
||||
const FRAME_TITLE = `Subframe for test browser_compartments.js ${Math.random()}`;
|
||||
|
||||
// This function is injected as source as a frameScript
|
||||
function frameScript() {
|
||||
|
@ -37,20 +30,6 @@ function frameScript() {
|
|||
Cu.reportError(ex.stack);
|
||||
}
|
||||
});
|
||||
|
||||
addMessageListener("compartments-test:setTitles", titles => {
|
||||
try {
|
||||
content.document.title = titles.data.parent;
|
||||
for (let i = 0; i < content.frames.length; ++i) {
|
||||
content.frames[i].postMessage({title: titles.data.frames}, "*");
|
||||
}
|
||||
console.log("content", "Done setting titles", content.document.title);
|
||||
sendAsyncMessage("compartments-test:setTitles");
|
||||
} catch (ex) {
|
||||
Cu.reportError("Error in content: " + ex);
|
||||
Cu.reportError(ex.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// A variant of `Assert` that doesn't spam the logs
|
||||
|
@ -93,6 +72,7 @@ function monotinicity_tester(source, testName) {
|
|||
};
|
||||
|
||||
let sanityCheck = function(prev, next) {
|
||||
info(`Sanity check: ${JSON.stringify(next, null, "\t")}`);
|
||||
if (prev == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -136,27 +116,18 @@ function monotinicity_tester(source, testName) {
|
|||
|
||||
// Sanity check on components data.
|
||||
let set = new Set();
|
||||
let map = new Map();
|
||||
let keys = [];
|
||||
for (let item of snapshot.componentsData) {
|
||||
let key = `{name: ${item.name}, addonId: ${item.addonId}, isSystem: ${item.isSystem}}`;
|
||||
keys.push(key);
|
||||
set.add(key);
|
||||
sanityCheck(previous.componentsMap.get(key), item);
|
||||
previous.componentsMap.set(key, item);
|
||||
|
||||
for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime"]) {
|
||||
SilentAssert.leq(item[k], snapshot.processData[k],
|
||||
`Sanity check (${testName}): component has a lower ${k} than process`);
|
||||
}
|
||||
|
||||
let key = `{name: ${item.name}, window: ${item.windowId}, addonId: ${item.addonId}, isSystem: ${item.isSystem}}`;
|
||||
if (set.has(key)) {
|
||||
// There are at least two components with the same name (e.g. about:blank).
|
||||
// Don't perform sanity checks on that name until we know how to make
|
||||
// the difference.
|
||||
map.delete(key);
|
||||
continue;
|
||||
}
|
||||
map.set(key, item);
|
||||
set.add(key);
|
||||
}
|
||||
for (let [key, item] of map) {
|
||||
sanityCheck(previous.componentsMap.get(key), item);
|
||||
previous.componentsMap.set(key, item);
|
||||
}
|
||||
info(`Deactivating deduplication check (Bug 1150045)`);
|
||||
if (false) {
|
||||
|
@ -176,7 +147,7 @@ add_task(function* test() {
|
|||
Assert.ok(!stats0.componentsData.find(stat => stat.name.indexOf(URL) != -1),
|
||||
"The url doesn't appear yet");
|
||||
|
||||
let newTab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
let newTab = gBrowser.addTab();
|
||||
let browser = newTab.linkedBrowser;
|
||||
// Setup monitoring in the tab
|
||||
info("Setting up monitoring in the tab");
|
||||
|
@ -195,40 +166,17 @@ add_task(function* test() {
|
|||
|
||||
let skipTotalUserTime = hasLowPrecision();
|
||||
|
||||
|
||||
while (true) {
|
||||
yield new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
// We may have race conditions with DOM loading.
|
||||
// Don't waste too much brainpower here, let's just ask
|
||||
// repeatedly for the title to be changed, until this works.
|
||||
info("Setting titles");
|
||||
yield promiseContentResponse(browser, "compartments-test:setTitles", {
|
||||
parent: PARENT_TITLE,
|
||||
frames: FRAME_TITLE
|
||||
});
|
||||
info("Titles set");
|
||||
|
||||
let stats = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null));
|
||||
|
||||
// While the webpage consists in three compartments, we should see only
|
||||
// one `PerformanceData` in `componentsData`. Its `name` is undefined
|
||||
// (could be either the main frame or one of its subframes), but its
|
||||
// `title` should be the title of the main frame.
|
||||
let frame = stats.componentsData.find(stat => stat.title == FRAME_TITLE);
|
||||
Assert.equal(frame, null, "Searching by title, the frames don't show up in the list of components");
|
||||
|
||||
let parent = stats.componentsData.find(stat => stat.title == PARENT_TITLE);
|
||||
if (!parent) {
|
||||
info("Searching by title, we didn't find the main frame");
|
||||
continue;
|
||||
}
|
||||
info("Searching by title, we found the main frame");
|
||||
|
||||
info(`Total user time: ${parent.totalUserTime}`);
|
||||
if (skipTotalUserTime || parent.totalUserTime > 1000) {
|
||||
let found = stats.componentsData.find(stat => {
|
||||
return (stat.name.indexOf(URL) != -1)
|
||||
&& (skipTotalUserTime || stat.totalUserTime > 1000)
|
||||
});
|
||||
if (found) {
|
||||
info(`Expected totalUserTime > 1000, got ${found.totalUserTime}`);
|
||||
break;
|
||||
}
|
||||
yield new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Subframe for test browser_compartments.html (do not change this title)
|
||||
</title>
|
||||
<script src="browser_compartments_script.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
Subframe loaded.
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче