mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 674779 - Add per-compartment CPU accounting. r=jandem, r=blassey, r=bz
This commit is contained in:
parent
a2edd2efd6
commit
2559ae292e
@ -19,9 +19,11 @@ CPOWTimer::~CPOWTimer()
|
||||
if (!obj)
|
||||
return;
|
||||
JSCompartment *compartment = js::GetObjectCompartment(obj);
|
||||
xpc::CompartmentPrivate *compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
|
||||
if (!compartmentPrivate)
|
||||
if (!compartment)
|
||||
return;
|
||||
PRIntervalTime time = PR_IntervalNow() - startInterval;
|
||||
compartmentPrivate->CPOWTime += time;
|
||||
js::PerformanceData *performance = js::GetPerformanceData(compartment);
|
||||
if (!performance)
|
||||
return;
|
||||
uint64_t time = PR_IntervalToMicroseconds(PR_IntervalNow() - startInterval);
|
||||
performance->cpowTime += time;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/js/ipc',
|
||||
'/js/public',
|
||||
'/js/src',
|
||||
'/js/xpconnect/src',
|
||||
'/js/xpconnect/wrappers',
|
||||
]
|
||||
|
@ -173,8 +173,9 @@ JS_GetCompartmentStats(JSRuntime *rt, CompartmentStatsVector &stats)
|
||||
return false;
|
||||
|
||||
CompartmentTimeStats *stat = &stats.back();
|
||||
stat->time = c.get()->totalTime;
|
||||
stat->compartment = c.get();
|
||||
stat->performance = stat->compartment->performance;
|
||||
stat->isSystem = stat->compartment->isSystem;
|
||||
stat->addonId = c.get()->addonId;
|
||||
if (rt->compartmentNameCallback) {
|
||||
(*rt->compartmentNameCallback)(rt, stat->compartment,
|
||||
@ -5801,3 +5802,11 @@ JS::GetObjectZone(JSObject *obj)
|
||||
{
|
||||
return obj->zone();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(PerformanceData*)
|
||||
js::GetPerformanceData(JSCompartment *c)
|
||||
{
|
||||
if (!c)
|
||||
return nullptr;
|
||||
return &c->performance;
|
||||
}
|
||||
|
@ -975,8 +975,8 @@ struct CompartmentTimeStats {
|
||||
char compartmentName[1024];
|
||||
JSAddonId *addonId;
|
||||
JSCompartment *compartment;
|
||||
uint64_t time; // microseconds
|
||||
uint64_t cpowTime; // microseconds
|
||||
bool isSystem;
|
||||
js::PerformanceData performance;
|
||||
};
|
||||
|
||||
typedef js::Vector<CompartmentTimeStats, 0, js::SystemAllocPolicy> CompartmentStatsVector;
|
||||
@ -5126,4 +5126,33 @@ StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
|
||||
/* Stopwatch-based CPU monitoring. */
|
||||
|
||||
namespace js {
|
||||
|
||||
/**
|
||||
* Reset any stopwatch currently measuring.
|
||||
*
|
||||
* This function is designed to be called when we process a new event.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
ResetStopwatches(JSRuntime*);
|
||||
|
||||
/**
|
||||
* Turn on/off stopwatch-based CPU monitoring.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetStopwatchActive(JSRuntime*, bool);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsStopwatchActive(JSRuntime*);
|
||||
|
||||
/**
|
||||
* Access the performance information stored in a compartment.
|
||||
*/
|
||||
extern JS_PUBLIC_API(PerformanceData*)
|
||||
GetPerformanceData(JSCompartment*);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsapi_h */
|
||||
|
@ -50,7 +50,6 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options =
|
||||
#endif
|
||||
global_(nullptr),
|
||||
enterCompartmentDepth(0),
|
||||
totalTime(0),
|
||||
data(nullptr),
|
||||
objectMetadataCallback(nullptr),
|
||||
lastAnimationTime(0),
|
||||
|
@ -170,18 +170,12 @@ struct JSCompartment
|
||||
int64_t startInterval;
|
||||
|
||||
public:
|
||||
int64_t totalTime;
|
||||
js::PerformanceData performance;
|
||||
void enter() {
|
||||
if (addonId && !enterCompartmentDepth) {
|
||||
startInterval = PRMJ_Now();
|
||||
}
|
||||
enterCompartmentDepth++;
|
||||
}
|
||||
void leave() {
|
||||
enterCompartmentDepth--;
|
||||
if (addonId && !enterCompartmentDepth) {
|
||||
totalTime += (PRMJ_Now() - startInterval);
|
||||
}
|
||||
}
|
||||
bool hasBeenEntered() { return !!enterCompartmentDepth; }
|
||||
|
||||
|
@ -479,6 +479,60 @@ struct PerThreadDataFriendFields
|
||||
template <typename T> friend class JS::Rooted;
|
||||
};
|
||||
|
||||
// Container for performance data
|
||||
struct PerformanceData {
|
||||
// Jank indicator.
|
||||
//
|
||||
// missedFrames[i] == number of times execution of this
|
||||
// compartment caused us to miss at least 2^i successive frames -
|
||||
// we assume that a frame lasts 16ms.
|
||||
uint64_t missedFrames[8];
|
||||
|
||||
// Total amount of time spent executing code in this compartment,
|
||||
// in microseconds, since process launch.
|
||||
uint64_t totalUserTime;
|
||||
uint64_t totalSystemTime;
|
||||
uint64_t ownUserTime;
|
||||
uint64_t ownSystemTime;
|
||||
uint64_t cpowTime;
|
||||
|
||||
// Total number of time code was executed in this compartment,
|
||||
// since process launch.
|
||||
uint64_t visits;
|
||||
|
||||
PerformanceData()
|
||||
: totalUserTime(0)
|
||||
, totalSystemTime(0)
|
||||
, ownUserTime(0)
|
||||
, ownSystemTime(0)
|
||||
, cpowTime(0)
|
||||
, visits(0)
|
||||
{
|
||||
memset(missedFrames, 0, sizeof(missedFrames));
|
||||
}
|
||||
PerformanceData(const PerformanceData& from)
|
||||
: totalUserTime(from.totalUserTime)
|
||||
, totalSystemTime(from.totalSystemTime)
|
||||
, ownUserTime(from.ownUserTime)
|
||||
, ownSystemTime(from.ownSystemTime)
|
||||
, cpowTime(from.cpowTime)
|
||||
, visits(from.visits)
|
||||
{
|
||||
memcpy(missedFrames, from.missedFrames, sizeof(missedFrames));
|
||||
}
|
||||
PerformanceData& operator=(const PerformanceData& from)
|
||||
{
|
||||
memcpy(missedFrames, from.missedFrames, sizeof(missedFrames));
|
||||
totalUserTime = from.totalUserTime;
|
||||
totalSystemTime = from.totalSystemTime;
|
||||
ownUserTime = from.ownUserTime;
|
||||
ownSystemTime = from.ownSystemTime;
|
||||
cpowTime = from.cpowTime;
|
||||
visits = from.visits;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jspubtd_h */
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
@ -52,6 +53,13 @@
|
||||
#include "vm/ScopeObject-inl.h"
|
||||
#include "vm/Stack-inl.h"
|
||||
|
||||
#if defined(XP_UNIX)
|
||||
#include <sys/resource.h>
|
||||
#elif defined(XP_WIN)
|
||||
#include <Processthreadsapi.h>
|
||||
#include <Windows.h>
|
||||
#endif // defined(XP_UNIX) || defined(XP_WIN)
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
@ -411,11 +419,191 @@ ExecuteState::pushInterpreterFrame(JSContext *cx)
|
||||
type_, evalInFrame_);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
// Implementation of per-compartment performance measurement.
|
||||
//
|
||||
//
|
||||
// All mutable state is stored in `Runtime::stopwatch`.
|
||||
struct AutoStopwatch MOZ_FINAL
|
||||
{
|
||||
// If the stopwatch is active, constructing an instance of
|
||||
// AutoStopwatch causes it to become the current owner of the
|
||||
// stopwatch.
|
||||
//
|
||||
// Previous owner is restored upon destruction.
|
||||
explicit inline AutoStopwatch(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: parent_(nullptr)
|
||||
, context_(cx)
|
||||
, descendentsUserTime_(0)
|
||||
, descendentsSystemTime_(0)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
JSRuntime *runtime = context_->runtime();
|
||||
if (!runtime->stopwatch.isActive)
|
||||
return;
|
||||
|
||||
|
||||
#if defined(XP_UNIX)
|
||||
int err = getrusage(RUSAGE_SELF, &timeStamp_);
|
||||
if (err)
|
||||
return;
|
||||
#elif defined(XP_WIN)
|
||||
FILETIME creationTime; // Ignored
|
||||
FILETIME exitTime; // Ignored
|
||||
|
||||
BOOL success = GetProcessTimes(GetCurrentProcess(),
|
||||
&creationTime, &exitTime,
|
||||
&kernelTimeStamp_, &userTimeStamp_);
|
||||
if (!success)
|
||||
return;
|
||||
#endif // defined(XP_UNIX) || defined(XP_WIN)
|
||||
|
||||
// Push on top of previous owner.
|
||||
parent_ = runtime->stopwatch.owner;
|
||||
runtime->stopwatch.owner = this;
|
||||
}
|
||||
|
||||
// If the stopwatch is active, destructing an instance of
|
||||
// AutoStopwatch causes ownership to return to the previous owner.
|
||||
inline ~AutoStopwatch()
|
||||
{
|
||||
JSRuntime *runtime = context_->runtime();
|
||||
if (runtime->stopwatch.owner != this) {
|
||||
// We are not the owner of the stopwatch, either because
|
||||
// we never acquired it, because we have entered a nested
|
||||
// event loop, or because some stopwatch lower on the
|
||||
// stack encountered an error.
|
||||
// If there is any ongoing measure, discard it.
|
||||
return;
|
||||
}
|
||||
|
||||
// If this destructor cannot proceed to completion without error,
|
||||
// prepare to cancel any ongoing measure.
|
||||
runtime->stopwatch.owner = nullptr;
|
||||
|
||||
// Durations in museconds of the total user/system time spent
|
||||
// by the entire process between construction and destruction
|
||||
// of this object.
|
||||
uint64_t totalUserTime, totalSystemTime;
|
||||
|
||||
#if defined(XP_UNIX)
|
||||
struct rusage stop;
|
||||
int err = getrusage(RUSAGE_SELF, &stop);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
totalUserTime =
|
||||
(stop.ru_utime.tv_usec - timeStamp_.ru_utime.tv_usec)
|
||||
+ (stop.ru_utime.tv_sec - timeStamp_.ru_utime.tv_sec) * 1000000;
|
||||
totalSystemTime =
|
||||
(stop.ru_stime.tv_usec - timeStamp_.ru_stime.tv_usec)
|
||||
+ (stop.ru_stime.tv_sec - timeStamp_.ru_stime.tv_sec) * 1000000;
|
||||
#elif defined(XP_WIN)
|
||||
FILETIME creationTime; // Ignored
|
||||
FILETIME exitTime; // Ignored
|
||||
FILETIME kernelTime;
|
||||
FILETIME userTime;
|
||||
BOOL success = GetProcessTimes(GetCurrentProcess(),
|
||||
&creationTime, &exitTime,
|
||||
&kernelTime, &userTime);
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
// Convert values to a data structure that supports arithmetics.
|
||||
ULARGE_INTEGER userTimeInt, userTimeStampInt;
|
||||
userTimeInt.LowPart = userTime.dwLowDateTime;
|
||||
userTimeInt.HighPart = userTime.dwHighDateTime;
|
||||
userTimeStampInt.LowPart = userTimeStamp_.dwLowDateTime;
|
||||
userTimeStampInt.HighPart = userTimeStamp_.dwHighDateTime;
|
||||
|
||||
totalUserTime = (userTimeInt.QuadPart - userTimeStampInt.QuadPart) / 10; // 100 ns to 1 mus
|
||||
|
||||
ULARGE_INTEGER kernelTimeInt, kernelTimeStampInt;
|
||||
kernelTimeInt.LowPart = kernelTime.dwLowDateTime;
|
||||
kernelTimeInt.HighPart = kernelTime.dwHighDateTime;
|
||||
kernelTimeStampInt.LowPart = kernelTimeStamp_.dwLowDateTime;
|
||||
kernelTimeStampInt.HighPart = kernelTimeStamp_.dwHighDateTime;
|
||||
|
||||
totalSystemTime = (kernelTimeInt.QuadPart - kernelTimeStampInt.QuadPart) / 10; // 100 ns to 1 mus
|
||||
#endif // defined(XP_UNIX) || defined (XP_WIN)
|
||||
|
||||
// Process durations.
|
||||
//
|
||||
// Note that durations are per-process, while our measures attempt
|
||||
// to be per-thread. In other words, the data we extract may be
|
||||
// badly skewed by activity on other threads. We can only hope that
|
||||
// things will eventually average out.
|
||||
|
||||
JSCompartment *compartment = context_->compartment();
|
||||
compartment->performance.visits++;
|
||||
compartment->performance.totalUserTime += totalUserTime;
|
||||
compartment->performance.totalSystemTime += totalSystemTime;
|
||||
|
||||
uint64_t ownUserTime = totalUserTime - descendentsUserTime_;
|
||||
uint64_t ownSystemTime = totalSystemTime - descendentsSystemTime_;
|
||||
compartment->performance.ownUserTime += ownUserTime;
|
||||
compartment->performance.ownSystemTime += ownSystemTime;
|
||||
|
||||
if (parent_) {
|
||||
// The time we just spent executing code in this compartment
|
||||
// should be substracted to determine the parent's own time.
|
||||
parent_->descendentsUserTime_ += totalUserTime;
|
||||
parent_->descendentsSystemTime_ += totalSystemTime;
|
||||
}
|
||||
|
||||
uint64_t totalDuration = totalUserTime + totalSystemTime;
|
||||
|
||||
// Store performance information in the compartment
|
||||
|
||||
uint64_t missedFrames = 16 * 1000; // Duration of one frame, i.e. 16ms in museconds
|
||||
for (size_t i = 0; i < ArrayLength(compartment->performance.missedFrames); ++i) {
|
||||
if (totalDuration < missedFrames)
|
||||
break;
|
||||
compartment->performance.missedFrames[i]++;
|
||||
missedFrames *= 2;
|
||||
}
|
||||
|
||||
runtime->stopwatch.owner = parent_;
|
||||
}
|
||||
|
||||
private:
|
||||
// The AutoStopwatch for the caller compartment.
|
||||
AutoStopwatch *parent_;
|
||||
|
||||
// The context with which this object was initialized.
|
||||
JSContext *context_;
|
||||
|
||||
// Total time spent so far executing compartments that have been
|
||||
// called by this compartment, in museconds. These values are
|
||||
// updated upon destruction of callee AutoStopwatch instances.
|
||||
uint64_t descendentsUserTime_;
|
||||
uint64_t descendentsSystemTime_;
|
||||
|
||||
// Kernel and user time at the time of construction of this
|
||||
// instance of AutoStopwatch. Unspecified if the stopwatch is not
|
||||
// active.
|
||||
#if defined(XP_UNIX)
|
||||
struct rusage timeStamp_;
|
||||
#elif defined(XP_WIN)
|
||||
FILETIME kernelTimeStamp_;
|
||||
FILETIME userTimeStamp_;
|
||||
#endif // defined(XP_UNIX) || defined(XP_WIN)
|
||||
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
js::RunScript(JSContext *cx, RunState &state)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
js::AutoStopwatch stopwatch(cx);
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
|
||||
SPSEntryMarker marker(cx->runtime(), state.script());
|
||||
|
||||
state.script()->ensureNonLazyCanonicalFunction(cx);
|
||||
|
@ -848,3 +848,21 @@ JS::UpdateJSRuntimeProfilerSampleBufferGen(JSRuntime *runtime, uint32_t generati
|
||||
runtime->setProfilerSampleBufferGen(generation);
|
||||
runtime->updateProfilerSampleBufferLapCount(lapCount);
|
||||
}
|
||||
|
||||
void
|
||||
js::ResetStopwatches(JSRuntime *rt)
|
||||
{
|
||||
rt->stopwatch.reset();
|
||||
}
|
||||
|
||||
void
|
||||
js::SetStopwatchActive(JSRuntime *rt, bool isActive)
|
||||
{
|
||||
rt->stopwatch.isActive = isActive;
|
||||
}
|
||||
|
||||
bool
|
||||
js::IsStopwatchActive(JSRuntime *rt)
|
||||
{
|
||||
return rt->stopwatch.isActive;
|
||||
}
|
||||
|
@ -566,6 +566,7 @@ class PerThreadData : public PerThreadDataFriendFields
|
||||
|
||||
class AutoLockForExclusiveAccess;
|
||||
|
||||
struct AutoStopwatch;
|
||||
} // namespace js
|
||||
|
||||
struct JSRuntime : public JS::shadow::Runtime,
|
||||
@ -1420,6 +1421,53 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
|
||||
/* Last time at which an animation was played for this runtime. */
|
||||
int64_t lastAnimationTime;
|
||||
|
||||
public:
|
||||
|
||||
/* ------------------------------------------
|
||||
Performance measurements
|
||||
------------------------------------------ */
|
||||
struct Stopwatch {
|
||||
/**
|
||||
* The current owner of the stopwatch.
|
||||
*
|
||||
* This is `nullptr` if we have not started executing JS code
|
||||
* in this tick yet, or if we are not using stopwatch
|
||||
* monitoring. If monitoring is activated, whenever we
|
||||
* instantiate an `AutoStopwatch`, it becomes the owner, and
|
||||
* whenever we destruct it, it returns ownership to the
|
||||
* previous owner.
|
||||
*
|
||||
* Note that `owner` is reset to `nullptr` if we start
|
||||
* processing a nested event. By design, this has the
|
||||
* side-effect of discarding any ongoing measurement that
|
||||
* would otherwise be completely skewed by the nested event
|
||||
* loop.
|
||||
*/
|
||||
js::AutoStopwatch *owner;
|
||||
|
||||
/**
|
||||
* `true` if stopwatch monitoring is active, `false` otherwise.
|
||||
*/
|
||||
bool isActive;
|
||||
|
||||
Stopwatch()
|
||||
: owner(nullptr)
|
||||
, isActive(false)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Reset the stopwatch.
|
||||
*
|
||||
* This method is meant to be called whewnever we start processing
|
||||
* an event, to ensure that stop any ongoing measurement that would
|
||||
* otherwise provide irrelevant results.
|
||||
*/
|
||||
void reset() {
|
||||
owner = nullptr;
|
||||
}
|
||||
};
|
||||
Stopwatch stopwatch;
|
||||
};
|
||||
|
||||
namespace js {
|
||||
|
@ -122,7 +122,7 @@ interface ScheduledGCCallback : nsISupports
|
||||
/**
|
||||
* interface of Components.utils
|
||||
*/
|
||||
[scriptable, uuid(2617a800-63c1-11e4-9803-0800200c9a66)]
|
||||
[scriptable, uuid(9b153002-3bc6-4d83-b825-64d21b13a098)]
|
||||
interface nsIXPCComponents_Utils : nsISupports
|
||||
{
|
||||
|
||||
@ -679,6 +679,14 @@ interface nsIXPCComponents_Utils : nsISupports
|
||||
* startup, measured with a monotonic clock.
|
||||
*/
|
||||
double now();
|
||||
|
||||
/*
|
||||
* Whether we are currently monitoring CPU use on this thread.
|
||||
*
|
||||
* Note that CPU monitoring is rather expensive, so it generally
|
||||
* not be active at all times.
|
||||
*/
|
||||
attribute boolean stopwatchMonitoring;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3529,6 +3529,22 @@ nsXPCComponents_Utils::Now(double *aRetval)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::SetStopwatchMonitoring(bool aValue)
|
||||
{
|
||||
JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->Runtime();
|
||||
SetStopwatchActive(rt, aValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::GetStopwatchMonitoring(bool* aResult)
|
||||
{
|
||||
JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->Runtime();
|
||||
*aResult = IsStopwatchActive(rt);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
|
@ -348,13 +348,6 @@ xpc::TraceXPCGlobal(JSTracer *trc, JSObject *obj)
|
||||
|
||||
namespace xpc {
|
||||
|
||||
uint64_t
|
||||
GetCompartmentCPOWMicroseconds(JSCompartment *compartment)
|
||||
{
|
||||
xpc::CompartmentPrivate *compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
|
||||
return compartmentPrivate ? PR_IntervalToMicroseconds(compartmentPrivate->CPOWTime) : 0;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal,
|
||||
JS::CompartmentOptions& aOptions)
|
||||
|
@ -626,6 +626,7 @@ public:
|
||||
void OnProcessNextEvent() {
|
||||
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
|
||||
mSlowScriptSecondHalf = false;
|
||||
js::ResetStopwatches(Get()->Runtime());
|
||||
}
|
||||
void OnAfterProcessNextEvent() {
|
||||
mSlowScriptCheckpoint = mozilla::TimeStamp();
|
||||
@ -3623,7 +3624,6 @@ public:
|
||||
, skipWriteToGlobalPrototype(false)
|
||||
, universalXPConnectEnabled(false)
|
||||
, forcePermissiveCOWs(false)
|
||||
, CPOWTime(0)
|
||||
, skipCOWCallableChecks(false)
|
||||
, scriptability(c)
|
||||
, scope(nullptr)
|
||||
@ -3677,9 +3677,6 @@ public:
|
||||
// Using it in production is inherently unsafe.
|
||||
bool forcePermissiveCOWs;
|
||||
|
||||
// A running count of how much time we've spent processing CPOWs.
|
||||
PRIntervalTime CPOWTime;
|
||||
|
||||
// Disables the XPConnect security checks that deny access to callables and
|
||||
// accessor descriptors on COWs. Do not use this unless you are bholley.
|
||||
bool skipCOWCallableChecks;
|
||||
|
@ -10,66 +10,158 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
function go() {
|
||||
let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
|
||||
.getService(Ci.nsICompartmentInfo);
|
||||
let compartments = compartmentInfo.getCompartments();
|
||||
let count = compartments.length;
|
||||
let addons = {};
|
||||
for (let i = 0; i < count; i++) {
|
||||
let compartment = compartments.queryElementAt(i, Ci.nsICompartment);
|
||||
if (addons[compartment.addonId]) {
|
||||
addons[compartment.addonId].time += compartment.time;
|
||||
addons[compartment.addonId].CPOWTime += compartment.CPOWTime;
|
||||
addons[compartment.addonId].compartments.push(compartment);
|
||||
} else {
|
||||
addons[compartment.addonId] = {
|
||||
time: compartment.time,
|
||||
CPOWTime: compartment.CPOWTime,
|
||||
compartments: [compartment]
|
||||
};
|
||||
}
|
||||
}
|
||||
let dataDiv = document.getElementById("data");
|
||||
for (let addon in addons) {
|
||||
let el = document.createElement("tr");
|
||||
let name = document.createElement("td");
|
||||
let time = document.createElement("td");
|
||||
let cpow = document.createElement("td");
|
||||
name.className = "addon";
|
||||
time.className = "time";
|
||||
cpow.className = "cpow";
|
||||
name.textContent = addon;
|
||||
AddonManager.getAddonByID(addon, function(a) {
|
||||
if (a) {
|
||||
name.textContent = a.name;
|
||||
}
|
||||
});
|
||||
time.textContent = addons[addon].time +"μs";
|
||||
cpow.textContent = addons[addon].CPOWTime +"μs";
|
||||
el.appendChild(time);
|
||||
el.appendChild(cpow);
|
||||
el.appendChild(name);
|
||||
let div = document.createElement("tr");
|
||||
for (let comp of addons[addon].compartments) {
|
||||
let c = document.createElement("tr");
|
||||
let name = document.createElement("td");
|
||||
let time = document.createElement("td");
|
||||
let cpow = document.createElement("td");
|
||||
name.className = "addon";
|
||||
time.className = "time";
|
||||
cpow.className = "cpow";
|
||||
name.textContent = comp.compartmentName;
|
||||
time.textContent = comp.time +"μs";
|
||||
cpow.textContent = comp.CPOWTime +"μs";
|
||||
c.appendChild(time);
|
||||
c.appendChild(cpow);
|
||||
c.appendChild(name);
|
||||
div.appendChild(c);
|
||||
div.className = "details";
|
||||
}
|
||||
el.addEventListener("click", function() { div.style.display = (div.style.display != "block" ? "block" : "none"); });
|
||||
el.appendChild(div);
|
||||
dataDiv.appendChild(el);
|
||||
}
|
||||
/**
|
||||
* The various measures we extract from a nsICompartment.
|
||||
*/
|
||||
const MEASURES = [
|
||||
{key: "totalUserTime", label: "Total user (µs)"},
|
||||
{key: "totalSystemTime", label: "Total system (µs)"},
|
||||
{key: "ownUserTime", label: "Own user (µs)"},
|
||||
{key: "ownSystemTime", label: "Own system (µs)"},
|
||||
{key: "cpowTime", label: "CPOW (µs)"},
|
||||
{key: "visits", label: "Activations"},
|
||||
];
|
||||
|
||||
/**
|
||||
* All information on a single owner (add-on, page, built-ins).
|
||||
*
|
||||
* @param {string} id A unique identifier.
|
||||
* @param {string} kind One of "<addon>", "<built-in>", "<page>".
|
||||
* @param {string|null} A name for this owner. Not expected for add-ons.
|
||||
*/
|
||||
function Owner(id, kind, name) {
|
||||
for (let {key} of MEASURES) {
|
||||
this[key] = 0;
|
||||
}
|
||||
this.compartments = [];
|
||||
this.id = id;
|
||||
this.kind = kind;
|
||||
this.name = name;
|
||||
}
|
||||
Owner.prototype = {
|
||||
add: function(compartment) {
|
||||
this.compartments.push(compartment);
|
||||
for (let {key} of MEASURES) {
|
||||
this[key] += compartment[key];
|
||||
}
|
||||
},
|
||||
promiseName: function() {
|
||||
if (this.kind != "<addon>") {
|
||||
return Promise.resolve(this.name);
|
||||
}
|
||||
return new Promise(resolve =>
|
||||
AddonManager.getAddonByID(this.id, a =>
|
||||
resolve(a?a.name:null)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function getStatistics() {
|
||||
let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
|
||||
.getService(Ci.nsICompartmentInfo);
|
||||
let compartments = compartmentInfo.getCompartments();
|
||||
let count = compartments.length;
|
||||
let data = {};
|
||||
for (let i = 0; i < count; i++) {
|
||||
let compartment = compartments.queryElementAt(i, Ci.nsICompartment);
|
||||
let kind, id, name;
|
||||
if (!compartment.isSystem) {
|
||||
name = id = compartment.compartmentName;
|
||||
kind = "<page>";
|
||||
} else if (compartment.addonId == "<non-addon>") {
|
||||
id = kind = name = "<built-in>";
|
||||
} else {
|
||||
name = id = compartment.addonId;
|
||||
kind = "<addon>";
|
||||
}
|
||||
let key = kind + ":" + id;
|
||||
let owner = data[key];
|
||||
if (!owner) {
|
||||
owner = data[key] = new Owner(id, kind, name);
|
||||
}
|
||||
owner.add(compartment);
|
||||
}
|
||||
return [data[k] for (k of Object.keys(data))].sort((a, b) => a.totalUserTime <= b.totalUserTime);
|
||||
}
|
||||
|
||||
function update() {
|
||||
try {
|
||||
console.log("Updating");
|
||||
|
||||
// Activate (or reactivate) monitoring
|
||||
Cu.stopwatchMonitoring = true;
|
||||
|
||||
let dataElt = document.getElementById("data");
|
||||
dataElt.innerHTML = "";
|
||||
|
||||
// Generate table headers
|
||||
let headerElt = document.createElement("tr");
|
||||
dataElt.appendChild(headerElt);
|
||||
for (let column of [...MEASURES, {key:"compartments", name: "Compartments"}, {key: "name", name: ""}]) {
|
||||
let el = document.createElement("td");
|
||||
el.classList.add(column.key);
|
||||
el.classList.add("header");
|
||||
el.textContent = column.label;
|
||||
headerElt.appendChild(el);
|
||||
}
|
||||
|
||||
// Generate table contents
|
||||
let data = getStatistics();
|
||||
console.log("Data", data);
|
||||
for (let item of data) {
|
||||
// Make sure that we don't show compartments with
|
||||
// no time spent.
|
||||
let show = false;
|
||||
for (let column of MEASURES) {
|
||||
if (item[column.key]) {
|
||||
show = true;
|
||||
}
|
||||
}
|
||||
if (!show) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let row = document.createElement("tr");
|
||||
row.classList.add(item.kind);
|
||||
dataElt.appendChild(row);
|
||||
|
||||
// Measures
|
||||
for (let column of MEASURES) {
|
||||
let el = document.createElement("td");
|
||||
el.classList.add(column.key);
|
||||
el.classList.add("contents");
|
||||
el.textContent = item[column.key];
|
||||
row.appendChild(el);
|
||||
}
|
||||
|
||||
// Number of compartments
|
||||
let el = document.createElement("td");
|
||||
el.classList.add("contents");
|
||||
el.classList.add("compartments");
|
||||
el.textContent = item.compartments.length;
|
||||
row.appendChild(el);
|
||||
|
||||
// Name
|
||||
el = document.createElement("td");
|
||||
el.classList.add("contents");
|
||||
el.classList.add("name");
|
||||
row.appendChild(el);
|
||||
item.promiseName().then(name => {
|
||||
name ? el.textContent = name : item.id;
|
||||
});
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
function stop() {
|
||||
Cu.stopwatchMonitoring = false;
|
||||
}
|
||||
|
||||
function go() {
|
||||
update();
|
||||
window.setInterval(update, 5000);
|
||||
window.addEventListener("beforeunload", stop);
|
||||
}
|
||||
|
@ -29,15 +29,19 @@
|
||||
color: gray;
|
||||
display: none;
|
||||
}
|
||||
tr.addons {
|
||||
background-color: white;
|
||||
}
|
||||
tr.builtins {
|
||||
background-color: rgb(1, 1, .5);
|
||||
}
|
||||
tr.pages {
|
||||
background-color: rgb(.5, 1, 1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<table id="data">
|
||||
<tr class="header">
|
||||
<td class="time">time</td>
|
||||
<td class="cpow">time in CPOWs</td>
|
||||
<td class="addon">name</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -4,6 +4,8 @@
|
||||
# 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/.
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
XPIDL_MODULE = 'compartments'
|
||||
@ -20,4 +22,6 @@ EXPORTS += [
|
||||
'nsCompartmentInfo.h'
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['tests/mochi/browser.ini']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -12,12 +12,16 @@
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "jspubtd.h"
|
||||
|
||||
class nsCompartment : public nsICompartment {
|
||||
public:
|
||||
nsCompartment(nsAString& aCompartmentName, nsAString& aAddonId,
|
||||
uint64_t aTime, uint64_t aCPOWTime)
|
||||
: mCompartmentName(aCompartmentName), mAddonId(aAddonId), mTime(aTime), mCPOWTime(aCPOWTime) {}
|
||||
nsCompartment(nsAString& aCompartmentName, nsAString& aAddonId, bool aIsSystem, js::PerformanceData aPerformanceData)
|
||||
: mCompartmentName(aCompartmentName)
|
||||
, mAddonId(aAddonId)
|
||||
, mIsSystem(aIsSystem)
|
||||
, mPerformanceData(aPerformanceData)
|
||||
{}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
@ -27,28 +31,68 @@ public:
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute unsigned long time; */
|
||||
NS_IMETHOD GetTime(uint64_t* aTime) MOZ_OVERRIDE {
|
||||
*aTime = mTime;
|
||||
return NS_OK;
|
||||
}
|
||||
/* readonly attribute wstring addon id; */
|
||||
NS_IMETHOD GetAddonId(nsAString& aAddonId) MOZ_OVERRIDE {
|
||||
aAddonId.Assign(mAddonId);
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute unsigned long CPOW time; */
|
||||
NS_IMETHOD GetCPOWTime(uint64_t* aCPOWTime) MOZ_OVERRIDE {
|
||||
*aCPOWTime = mCPOWTime;
|
||||
/* readonly attribute unsigned long long totalUserTime; */
|
||||
NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) {
|
||||
*aTotalUserTime = mPerformanceData.totalUserTime;
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute unsigned long long totalSystemTime; */
|
||||
NS_IMETHOD GetTotalSystemTime(uint64_t *aTotalSystemTime) {
|
||||
*aTotalSystemTime = mPerformanceData.totalSystemTime;
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute unsigned long long ownUserTime; */
|
||||
NS_IMETHOD GetOwnUserTime(uint64_t *aOwnUserTime) {
|
||||
*aOwnUserTime = mPerformanceData.ownUserTime;
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute unsigned long long ownSystemTime; */
|
||||
NS_IMETHOD GetOwnSystemTime(uint64_t *aOwnSystemTime) {
|
||||
*aOwnSystemTime = mPerformanceData.ownSystemTime;
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute unsigned long long CPOWTime; */
|
||||
NS_IMETHOD GetCPOWTime(uint64_t *aCpowTime) {
|
||||
*aCpowTime = mPerformanceData.cpowTime;
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute unsigned long long visits; */
|
||||
NS_IMETHOD GetVisits(uint64_t *aVisits) {
|
||||
*aVisits = mPerformanceData.visits;
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* unsigned long long getMissedFrames (in unsigned long i); */
|
||||
NS_IMETHOD GetMissedFrames(uint32_t i, uint64_t *_retval) {
|
||||
if (i >= mozilla::ArrayLength(mPerformanceData.missedFrames)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*_retval = mPerformanceData.missedFrames[i];
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
NS_IMETHOD GetIsSystem(bool *_retval) {
|
||||
*_retval = mIsSystem;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mCompartmentName;
|
||||
nsString mAddonId;
|
||||
uint64_t mTime;
|
||||
uint64_t mCPOWTime;
|
||||
bool mIsSystem;
|
||||
js::PerformanceData mPerformanceData;
|
||||
|
||||
virtual ~nsCompartment() {}
|
||||
};
|
||||
|
||||
@ -77,17 +121,18 @@ nsCompartmentInfo::GetCompartments(nsIArray** aCompartments)
|
||||
|
||||
size_t num = stats.length();
|
||||
for (size_t pos = 0; pos < num; pos++) {
|
||||
CompartmentTimeStats *c = &stats[pos];
|
||||
nsString addonId;
|
||||
if (stats[pos].addonId) {
|
||||
AssignJSFlatString(addonId, (JSFlatString*)stats[pos].addonId);
|
||||
if (c->addonId) {
|
||||
AssignJSFlatString(addonId, (JSFlatString*)c->addonId);
|
||||
} else {
|
||||
addonId.AssignLiteral("<non-addon>");
|
||||
}
|
||||
|
||||
uint32_t cpowTime = xpc::GetCompartmentCPOWMicroseconds(stats[pos].compartment);
|
||||
nsCString compartmentName(stats[pos].compartmentName);
|
||||
nsCString compartmentName(c->compartmentName);
|
||||
NS_ConvertUTF8toUTF16 name(compartmentName);
|
||||
compartments->AppendElement(new nsCompartment(name, addonId, stats[pos].time, cpowTime), false);
|
||||
|
||||
compartments->AppendElement(new nsCompartment(name, addonId, c->isSystem, c->performance), false);
|
||||
}
|
||||
compartments.forget(aCompartments);
|
||||
return NS_OK;
|
||||
|
@ -7,16 +7,52 @@
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIArray.idl"
|
||||
|
||||
[scriptable, uuid(13dd4c09-ff11-4943-8dc2-d96eb69c963b)]
|
||||
[scriptable, uuid(3a23d383-052e-4199-8914-74c037fe359e)]
|
||||
interface nsICompartment : nsISupports {
|
||||
/* name of compartment */
|
||||
readonly attribute AString compartmentName;
|
||||
/* time spent executing code in this compartment in microseconds */
|
||||
readonly attribute unsigned long long time;
|
||||
|
||||
/* the id of the addon associated with this compartment, or null */
|
||||
readonly attribute AString addonId;
|
||||
/* time spent processing CPOWs in microseconds */
|
||||
|
||||
/*
|
||||
Total amount of time spent executing code in this compartment, in
|
||||
microseconds.
|
||||
|
||||
Note that these durations are only computed while
|
||||
Components.utils.stopwatchMonitoring == true and that they are never
|
||||
reset to 0.
|
||||
*/
|
||||
readonly attribute unsigned long long totalUserTime;
|
||||
readonly attribute unsigned long long totalSystemTime;
|
||||
readonly attribute unsigned long long ownUserTime;
|
||||
readonly attribute unsigned long long ownSystemTime;
|
||||
readonly attribute unsigned long long CPOWTime;
|
||||
|
||||
/* Number of times we have executed code in this compartment.
|
||||
Updated only while Components.utils.stopwatchMonitoring == true,
|
||||
never reset to 0.
|
||||
*/
|
||||
readonly attribute unsigned long long visits;
|
||||
|
||||
/* `true` if this is a system compartment (either an add-on or a built-in).*/
|
||||
readonly attribute bool isSystem;
|
||||
|
||||
/**
|
||||
* The number of times execution of code in this compartment has apparently
|
||||
* caused frame drops.
|
||||
*
|
||||
* getMissedFrames(0): number of times we have dropped at least 1 frame
|
||||
* getMissedFrames(1): number of times we have dropped at least 2 successive frames
|
||||
* ...
|
||||
* getMissedFrames(i): number of times we have dropped at least 2^i successive frames
|
||||
*
|
||||
* Updated only while Components.utils.stopwatchMonitoring == true,
|
||||
* never reset to 0.
|
||||
*/
|
||||
unsigned long long getMissedFrames(in unsigned long i);
|
||||
/* Number of values that may be consulted in `getMissedFrames`.*/
|
||||
const unsigned long MISSED_FRAMES_RANGE = 8;
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)]
|
||||
|
10
toolkit/components/aboutcompartments/tests/mochi/browser.ini
Normal file
10
toolkit/components/aboutcompartments/tests/mochi/browser.ini
Normal file
@ -0,0 +1,10 @@
|
||||
# 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]
|
||||
support-files =
|
||||
browser_compartments.html
|
||||
content.js
|
||||
|
||||
[browser_compartments.js]
|
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
browser_compartments.html
|
||||
</title>
|
||||
<script type="text/javascript">
|
||||
// Use some CPU
|
||||
// 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 <= 200) {
|
||||
array[i%2] = i++;
|
||||
}
|
||||
console.log("Arbitrary value", array);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
browser_compartments.html
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,123 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
const ROOT = getRootDirectory(gTestPath);
|
||||
const FRAME_SCRIPTS = [
|
||||
ROOT + "content.js",
|
||||
];
|
||||
const URL = "chrome://mochitests/content/browser/toolkit/components/aboutcompartments/tests/mochi/browser_compartments.html";
|
||||
|
||||
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageListenerManager);
|
||||
|
||||
for (let script of FRAME_SCRIPTS) {
|
||||
mm.loadFrameScript(script, true);
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
for (let script of FRAME_SCRIPTS) {
|
||||
mm.removeDelayedFrameScript(script, true);
|
||||
}
|
||||
});
|
||||
|
||||
function promiseContentResponse(browser, name, message) {
|
||||
let mm = browser.messageManager;
|
||||
let promise = new Promise(resolve => {
|
||||
function removeListener() {
|
||||
mm.removeMessageListener(name, listener);
|
||||
}
|
||||
|
||||
function listener(msg) {
|
||||
removeListener();
|
||||
resolve(msg.data);
|
||||
}
|
||||
|
||||
mm.addMessageListener(name, listener);
|
||||
registerCleanupFunction(removeListener);
|
||||
});
|
||||
mm.sendAsyncMessage(name, message);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function getStatistics() {
|
||||
let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
|
||||
.getService(Ci.nsICompartmentInfo);
|
||||
let data = compartmentInfo.getCompartments();
|
||||
let result = [];
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
result.push(data.queryElementAt(i, Ci.nsICompartment));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function* promiseTabLoadEvent(tab, url)
|
||||
{
|
||||
return new Promise(function (resolve, reject) {
|
||||
function handleLoadEvent(event) {
|
||||
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
|
||||
event.target.location.href == "about:blank" ||
|
||||
(url && event.target.location.href != url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tab.linkedBrowser.removeEventListener("load", handleLoadEvent, true);
|
||||
resolve(event);
|
||||
}
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", handleLoadEvent, true, true);
|
||||
if (url)
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* init() {
|
||||
let monitoring = Cu.stopwatchMonitoring;
|
||||
Cu.stopwatchMonitoring = true;
|
||||
registerCleanupFunction(() => {
|
||||
Cu.stopwatchMonitoring = monitoring;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test() {
|
||||
info("Extracting initial state");
|
||||
let stats0 = getStatistics();
|
||||
Assert.notEqual(stats0.length, 0, "There is more than one compartment");
|
||||
Assert.ok(!stats0.find(stat => stat.compartmentName.indexOf(URL) != -1),
|
||||
"The url doesn't appear yet");
|
||||
stats0.forEach(stat => {
|
||||
Assert.ok(stat.totalUserTime >= stat.ownUserTime, "Total >= own user time: " + stat.compartmentName);
|
||||
Assert.ok(stat.totalSystemTime >= stat.ownSystemTime, "Total >= own system time: " + stat.compartmentName);
|
||||
});
|
||||
|
||||
let newTab = gBrowser.addTab();
|
||||
let browser = newTab.linkedBrowser;
|
||||
// Setup monitoring in the tab
|
||||
info("Setting up monitoring in the tab");
|
||||
let childMonitoring = yield promiseContentResponse(browser, "compartments-test:setMonitoring", true);
|
||||
|
||||
info("Waiting for load to be complete");
|
||||
yield promiseTabLoadEvent(newTab, URL);
|
||||
|
||||
if (!gMultiProcessBrowser) {
|
||||
// In non-e10s mode, the stats are counted in the single process
|
||||
let stats1 = getStatistics();
|
||||
let pageStats = stats1.find(stat => stat.compartmentName.indexOf(URL) != -1);
|
||||
Assert.notEqual(pageStats, null, "The new page appears in the single-process statistics");
|
||||
Assert.notEqual(pageStats.totalUserTime, 0, "Total single-process user time is > 0");
|
||||
}
|
||||
|
||||
// Regardless of e10s, the stats should be accessible in the content process (or pseudo-process)
|
||||
let stats2 = yield promiseContentResponse(browser, "compartments-test:getCompartments", null);
|
||||
let pageStats = stats2.find(stat => stat.compartmentName.indexOf(URL) != -1);
|
||||
Assert.notEqual(pageStats, null, "The new page appears in the content statistics");
|
||||
Assert.notEqual(pageStats.totalUserTime, 0, "Total content user time is > 0");
|
||||
Assert.ok(pageStats.totalUserTime >= 1000, "Total content user time is at least 1ms");
|
||||
|
||||
// Cleanup
|
||||
yield promiseContentResponse(browser, "compartments-test:setMonitoring", childMonitoring);
|
||||
gBrowser.removeTab(newTab);
|
||||
});
|
36
toolkit/components/aboutcompartments/tests/mochi/content.js
Normal file
36
toolkit/components/aboutcompartments/tests/mochi/content.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
|
||||
|
||||
addMessageListener("compartments-test:setMonitoring", msg => {
|
||||
let stopwatchMonitoring = Cu.stopwatchMonitoring;
|
||||
Cu.stopwatchMonitoring = msg.data;
|
||||
sendAsyncMessage("compartments-test:setMonitoring", stopwatchMonitoring);
|
||||
});
|
||||
|
||||
addMessageListener("compartments-test:getCompartments", () => {
|
||||
let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
|
||||
.getService(Ci.nsICompartmentInfo);
|
||||
let data = compartmentInfo.getCompartments();
|
||||
let result = [];
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
let xpcom = data.queryElementAt(i, Ci.nsICompartment);
|
||||
let object = {};
|
||||
for (let k of Object.keys(xpcom)) {
|
||||
let value = xpcom[k];
|
||||
if (typeof value != "function") {
|
||||
object[k] = value;
|
||||
}
|
||||
}
|
||||
object.frames = [];
|
||||
for (let i = 0; i < Ci.nsICompartment.MISSED_FRAMES_RANGE; ++i) {
|
||||
object.frames[i] = xpcom.getMissedFrames(i);
|
||||
}
|
||||
result.push(object);
|
||||
}
|
||||
sendAsyncMessage("compartments-test:getCompartments", result);
|
||||
});
|
@ -26,11 +26,13 @@ let AddonWatcher = {
|
||||
}
|
||||
|
||||
if (this._callback) {
|
||||
// Already initialized
|
||||
return;
|
||||
}
|
||||
|
||||
this._interval = Preferences.get("browser.addon-watch.interval", 15000);
|
||||
if (this._interval == -1) {
|
||||
// Deactivated by preferences
|
||||
return;
|
||||
}
|
||||
|
||||
@ -41,6 +43,9 @@ let AddonWatcher = {
|
||||
// probably some malformed JSON, ignore and carry on
|
||||
this._ignoreList = new Set();
|
||||
}
|
||||
|
||||
// Start monitoring
|
||||
Cu.stopwatchMonitoring = true;
|
||||
this._timer.initWithCallback(this._checkAddons.bind(this), this._interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
|
||||
},
|
||||
uninit: function() {
|
||||
@ -48,6 +53,7 @@ let AddonWatcher = {
|
||||
this._timer.cancel();
|
||||
this._timer = null;
|
||||
}
|
||||
Cu.stopwatchMonitoring = false;
|
||||
},
|
||||
_checkAddons: function() {
|
||||
let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
|
||||
|
Loading…
Reference in New Issue
Block a user