зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1311212 - Add dead CPOW debugging facility (r=mrbkap)
This commit is contained in:
Родитель
ef9a4d78b7
Коммит
cbd15a0459
|
@ -366,14 +366,17 @@ function dead_test(finish)
|
|||
return;
|
||||
}
|
||||
|
||||
let gcTrigger = function() {
|
||||
// Force the GC to dead-ify the thing.
|
||||
content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
}
|
||||
|
||||
{
|
||||
let thing = { value: "Gonna croak" };
|
||||
sendAsyncMessage("cpows:dead", null, { thing });
|
||||
sendAsyncMessage("cpows:dead", null, { thing, gcTrigger });
|
||||
}
|
||||
// Force the GC to dead-ify the thing.
|
||||
content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
|
||||
addMessageListener("cpows:dead_done", finish);
|
||||
}
|
||||
|
|
|
@ -418,14 +418,18 @@
|
|||
}
|
||||
|
||||
function recvDead(msg) {
|
||||
try {
|
||||
msg.objects.thing.value;
|
||||
ok(false, "Should have been a dead CPOW");
|
||||
} catch(e if /dead CPOW/.test(String(e))) {
|
||||
ok(true, "Got the expected dead CPOW");
|
||||
ok(e.stack, "The exception has a stack");
|
||||
}
|
||||
msg.target.messageManager.sendAsyncMessage("cpows:dead_done");
|
||||
// Need to do this in a separate turn of the event loop.
|
||||
setTimeout(() => {
|
||||
msg.objects.gcTrigger();
|
||||
try {
|
||||
msg.objects.thing.value;
|
||||
ok(false, "Should have been a dead CPOW");
|
||||
} catch(e if /dead CPOW/.test(String(e))) {
|
||||
ok(true, "Got the expected dead CPOW");
|
||||
ok(e.stack, "The exception has a stack");
|
||||
}
|
||||
msg.target.messageManager.sendAsyncMessage("cpows:dead_done");
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function run_tests(type) {
|
||||
|
|
|
@ -89,6 +89,9 @@ NewJavaScriptChild();
|
|||
void
|
||||
ReleaseJavaScriptChild(PJavaScriptChild* child);
|
||||
|
||||
void
|
||||
AfterProcessTask();
|
||||
|
||||
} // namespace jsipc
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -26,10 +26,17 @@ UpdateChildWeakPointersBeforeSweepingZoneGroup(JSContext* cx, void* data)
|
|||
static_cast<JavaScriptChild*>(data)->updateWeakPointers();
|
||||
}
|
||||
|
||||
static void
|
||||
TraceChild(JSTracer* trc, void* data)
|
||||
{
|
||||
static_cast<JavaScriptChild*>(data)->trace(trc);
|
||||
}
|
||||
|
||||
JavaScriptChild::~JavaScriptChild()
|
||||
{
|
||||
JSContext* cx = dom::danger::GetJSContext();
|
||||
JS_RemoveWeakPointerZoneGroupCallback(cx, UpdateChildWeakPointersBeforeSweepingZoneGroup);
|
||||
JS_RemoveExtraGCRootsTracer(cx, TraceChild, this);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -42,9 +49,16 @@ JavaScriptChild::init()
|
|||
|
||||
JSContext* cx = dom::danger::GetJSContext();
|
||||
JS_AddWeakPointerZoneGroupCallback(cx, UpdateChildWeakPointersBeforeSweepingZoneGroup, this);
|
||||
JS_AddExtraGCRootsTracer(cx, TraceChild, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JavaScriptChild::trace(JSTracer* trc)
|
||||
{
|
||||
objects_.trace(trc, strongReferenceObjIdMinimum_);
|
||||
}
|
||||
|
||||
void
|
||||
JavaScriptChild::updateWeakPointers()
|
||||
{
|
||||
|
@ -61,6 +75,13 @@ JavaScriptChild::scopeForTargetObjects()
|
|||
return xpc::PrivilegedJunkScope();
|
||||
}
|
||||
|
||||
bool
|
||||
JavaScriptChild::RecvDropTemporaryStrongReferences(const uint64_t& upToObjId)
|
||||
{
|
||||
strongReferenceObjIdMinimum_ = upToObjId + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
PJavaScriptChild*
|
||||
mozilla::jsipc::NewJavaScriptChild()
|
||||
{
|
||||
|
|
|
@ -17,9 +17,11 @@ namespace jsipc {
|
|||
class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
|
||||
{
|
||||
public:
|
||||
JavaScriptChild() : strongReferenceObjIdMinimum_(0) {}
|
||||
virtual ~JavaScriptChild();
|
||||
|
||||
bool init();
|
||||
void trace(JSTracer* trc);
|
||||
void updateWeakPointers();
|
||||
|
||||
void drop(JSObject* obj);
|
||||
|
@ -30,9 +32,16 @@ class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
|
|||
virtual bool isParent() override { return false; }
|
||||
virtual JSObject* scopeForTargetObjects() override;
|
||||
|
||||
bool RecvDropTemporaryStrongReferences(const uint64_t& upToObjId) override;
|
||||
|
||||
private:
|
||||
bool fail(JSContext* cx, ReturnStatus* rs);
|
||||
bool ok(ReturnStatus* rs);
|
||||
|
||||
// JavaScriptChild will keep strong references to JS objects that are
|
||||
// referenced by the parent only if their ID is >=
|
||||
// strongReferenceObjIdMinimum_.
|
||||
uint64_t strongReferenceObjIdMinimum_;
|
||||
};
|
||||
|
||||
} // namespace jsipc
|
||||
|
|
|
@ -172,15 +172,17 @@ JavaScriptParent::scopeForTargetObjects()
|
|||
return xpc::UnprivilegedJunkScope();
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
JavaScriptParent::CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx)
|
||||
void
|
||||
JavaScriptParent::afterProcessTask()
|
||||
{
|
||||
ContentParent* contentParent = aCtx->GetContentParent();
|
||||
nsAutoPtr<PJavaScriptParent> actor(contentParent->AllocPJavaScriptParent());
|
||||
if (!actor || !contentParent->RecvPJavaScriptConstructor(actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return actor.forget();
|
||||
if (savedNextCPOWNumber_ == nextCPOWNumber_)
|
||||
return;
|
||||
|
||||
savedNextCPOWNumber_ = nextCPOWNumber_;
|
||||
|
||||
MOZ_ASSERT(nextCPOWNumber_ > 0);
|
||||
if (active())
|
||||
Unused << SendDropTemporaryStrongReferences(nextCPOWNumber_ - 1);
|
||||
}
|
||||
|
||||
PJavaScriptParent*
|
||||
|
@ -199,3 +201,12 @@ mozilla::jsipc::ReleaseJavaScriptParent(PJavaScriptParent* parent)
|
|||
{
|
||||
static_cast<JavaScriptParent*>(parent)->decref();
|
||||
}
|
||||
|
||||
void
|
||||
mozilla::jsipc::AfterProcessTask()
|
||||
{
|
||||
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
|
||||
if (PJavaScriptParent* p = LoneManagedOrNullAsserts(cp->ManagedPJavaScriptParent()))
|
||||
static_cast<JavaScriptParent*>(p)->afterProcessTask();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace jsipc {
|
|||
class JavaScriptParent : public JavaScriptBase<PJavaScriptParent>
|
||||
{
|
||||
public:
|
||||
JavaScriptParent() : savedNextCPOWNumber_(1) {}
|
||||
virtual ~JavaScriptParent();
|
||||
|
||||
bool init();
|
||||
|
@ -25,13 +26,14 @@ class JavaScriptParent : public JavaScriptBase<PJavaScriptParent>
|
|||
void drop(JSObject* obj);
|
||||
|
||||
bool allowMessage(JSContext* cx) override;
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) override;
|
||||
void afterProcessTask();
|
||||
|
||||
protected:
|
||||
virtual bool isParent() override { return true; }
|
||||
virtual JSObject* scopeForTargetObjects() override;
|
||||
|
||||
private:
|
||||
uint64_t savedNextCPOWNumber_;
|
||||
};
|
||||
|
||||
} // namespace jsipc
|
||||
|
|
|
@ -33,10 +33,12 @@ IdToObjectMap::init()
|
|||
}
|
||||
|
||||
void
|
||||
IdToObjectMap::trace(JSTracer* trc)
|
||||
IdToObjectMap::trace(JSTracer* trc, uint64_t minimimId)
|
||||
{
|
||||
for (Table::Range r(table_.all()); !r.empty(); r.popFront())
|
||||
JS::TraceEdge(trc, &r.front().value(), "ipc-object");
|
||||
for (Table::Range r(table_.all()); !r.empty(); r.popFront()) {
|
||||
if (r.front().key().serialNumber() >= minimimId)
|
||||
JS::TraceEdge(trc, &r.front().value(), "ipc-object");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -145,7 +147,8 @@ bool JavaScriptShared::sStackLoggingEnabled;
|
|||
|
||||
JavaScriptShared::JavaScriptShared()
|
||||
: refcount_(1),
|
||||
nextSerialNumber_(1)
|
||||
nextSerialNumber_(1),
|
||||
nextCPOWNumber_(1)
|
||||
{
|
||||
if (!sLoggingInitialized) {
|
||||
sLoggingInitialized = true;
|
||||
|
|
|
@ -91,7 +91,7 @@ class IdToObjectMap
|
|||
IdToObjectMap();
|
||||
|
||||
bool init();
|
||||
void trace(JSTracer* trc);
|
||||
void trace(JSTracer* trc, uint64_t minimumId = 0);
|
||||
void sweep();
|
||||
|
||||
bool add(ObjectId id, JSObject* obj);
|
||||
|
@ -200,6 +200,10 @@ class JavaScriptShared : public CPOWManager
|
|||
|
||||
uint64_t nextSerialNumber_;
|
||||
|
||||
// nextCPOWNumber_ should be the value of nextSerialNumber_ in the other
|
||||
// process. The next new CPOW we get should have this serial number.
|
||||
uint64_t nextCPOWNumber_;
|
||||
|
||||
// CPOW references can be weak, and any object we store in a map may be
|
||||
// GCed (at which point the CPOW will report itself "dead" to the owner).
|
||||
// This means that we don't want to store any js::Wrappers in the CPOW map,
|
||||
|
|
|
@ -51,6 +51,9 @@ both:
|
|||
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
child:
|
||||
async DropTemporaryStrongReferences(uint64_t upToObjId);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,22 @@ using namespace mozilla::jsipc;
|
|||
using mozilla::dom::AutoJSAPI;
|
||||
using mozilla::dom::AutoEntryScript;
|
||||
|
||||
static void
|
||||
MaybeForceDebugGC()
|
||||
{
|
||||
static bool sEnvVarInitialized = false;
|
||||
static bool sDebugGCs = false;
|
||||
|
||||
if (!sEnvVarInitialized)
|
||||
sDebugGCs = !!PR_GetEnv("MOZ_DEBUG_DEAD_CPOWS");
|
||||
|
||||
if (sDebugGCs) {
|
||||
JSContext* cx = nsXPConnect::GetContextInstance()->Context();
|
||||
PrepareForFullGC(cx);
|
||||
GCForReason(cx, GC_NORMAL, gcreason::COMPONENT_UTILS);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperAnswer::fail(AutoJSAPI& jsapi, ReturnStatus* rs)
|
||||
{
|
||||
|
@ -85,6 +101,8 @@ WrapperAnswer::deadCPOW(AutoJSAPI& jsapi, ReturnStatus* rs)
|
|||
bool
|
||||
WrapperAnswer::RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -116,6 +134,8 @@ bool
|
|||
WrapperAnswer::RecvGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar,
|
||||
ReturnStatus* rs, PPropertyDescriptor* out)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -146,6 +166,8 @@ bool
|
|||
WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar,
|
||||
ReturnStatus* rs, PPropertyDescriptor* out)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -176,6 +198,8 @@ bool
|
|||
WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVar,
|
||||
const PPropertyDescriptor& descriptor, ReturnStatus* rs)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -204,6 +228,8 @@ WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVa
|
|||
bool
|
||||
WrapperAnswer::RecvDelete(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -229,6 +255,8 @@ bool
|
|||
WrapperAnswer::RecvHas(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs,
|
||||
bool* foundp)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -254,6 +282,8 @@ bool
|
|||
WrapperAnswer::RecvHasOwn(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs,
|
||||
bool* foundp)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -279,6 +309,8 @@ bool
|
|||
WrapperAnswer::RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
|
||||
const JSIDVariant& idVar, ReturnStatus* rs, JSVariant* result)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
// We may run scripted getters.
|
||||
AutoEntryScript aes(scopeForTargetObjects(),
|
||||
"Cross-Process Object Wrapper 'get'");
|
||||
|
@ -316,6 +348,8 @@ bool
|
|||
WrapperAnswer::RecvSet(const ObjectId& objId, const JSIDVariant& idVar, const JSVariant& value,
|
||||
const JSVariant& receiverVar, ReturnStatus* rs)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
// We may run scripted setters.
|
||||
AutoEntryScript aes(scopeForTargetObjects(),
|
||||
"Cross-Process Object Wrapper 'set'");
|
||||
|
@ -349,6 +383,8 @@ WrapperAnswer::RecvSet(const ObjectId& objId, const JSIDVariant& idVar, const JS
|
|||
bool
|
||||
WrapperAnswer::RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs, bool* result)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -377,6 +413,8 @@ WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId,
|
|||
JSVariant* result,
|
||||
nsTArray<JSParam>* outparams)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoEntryScript aes(scopeForTargetObjects(),
|
||||
"Cross-Process Object Wrapper call/construct");
|
||||
JSContext* cx = aes.cx();
|
||||
|
@ -475,6 +513,8 @@ WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId,
|
|||
bool
|
||||
WrapperAnswer::RecvHasInstance(const ObjectId& objId, const JSVariant& vVar, ReturnStatus* rs, bool* bp)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -500,6 +540,8 @@ bool
|
|||
WrapperAnswer::RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
|
||||
uint32_t* classValue)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
*classValue = uint32_t(js::ESClass::Other);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
|
@ -525,6 +567,8 @@ bool
|
|||
WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
|
||||
uint32_t* ans)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
*ans = uint32_t(IsArrayAnswer::NotArray);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
|
@ -549,6 +593,8 @@ WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
|
|||
bool
|
||||
WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -570,6 +616,8 @@ WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name)
|
|||
bool
|
||||
WrapperAnswer::RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
*result = NullVariant();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
|
@ -597,6 +645,8 @@ bool
|
|||
WrapperAnswer::RecvGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* rs, bool* isOrdinary,
|
||||
ObjectOrNullVariant* result)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
*result = NullVariant();
|
||||
*isOrdinary = false;
|
||||
|
||||
|
@ -625,6 +675,8 @@ bool
|
|||
WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
|
||||
nsString* source, uint32_t* flags)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -651,6 +703,8 @@ bool
|
|||
WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
|
||||
ReturnStatus* rs, nsTArray<JSIDVariant>* ids)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -681,6 +735,8 @@ bool
|
|||
WrapperAnswer::RecvInstanceOf(const ObjectId& objId, const JSIID& iid, ReturnStatus* rs,
|
||||
bool* instanceof)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
@ -708,6 +764,8 @@ bool
|
|||
WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID,
|
||||
const int& depth, ReturnStatus* rs, bool* instanceof)
|
||||
{
|
||||
MaybeForceDebugGC();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
|
|
|
@ -1203,6 +1203,8 @@ WrapperOwner::fromRemoteObjectVariant(JSContext* cx, RemoteObject objVar)
|
|||
if (!cpows_.add(objId, obj))
|
||||
return nullptr;
|
||||
|
||||
nextCPOWNumber_ = objId.serialNumber() + 1;
|
||||
|
||||
// Incref once we know the decref will be called.
|
||||
incref();
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ProcessHangMonitor.h"
|
||||
|
@ -3612,6 +3613,8 @@ XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth)
|
|||
// Now that we are certain that the event is complete,
|
||||
// we can flush any ongoing performance measurement.
|
||||
js::FlushPerformanceMonitoring(Get()->Context());
|
||||
|
||||
mozilla::jsipc::AfterProcessTask();
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
|
Загрузка…
Ссылка в новой задаче