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:
Ryan VanderMeulen 2015-05-29 12:59:23 -04:00
Родитель 2fd90bf639
Коммит 1dc9d316cf
22 изменённых файлов: 245 добавлений и 580 удалений

Просмотреть файл

@ -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>