Bug 1577146 - Consider debugger weak maps when calculating which compartments we think are live r=sfink

Differential Revision: https://phabricator.services.mozilla.com/D44399

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jon Coppeard 2019-09-06 10:45:30 +00:00
Родитель 61645c287f
Коммит bdab0a4eea
5 изменённых файлов: 90 добавлений и 11 удалений

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

@ -68,6 +68,10 @@ enum class ResumeMode {
class DebugScript;
class DebuggerVector;
using CompartmentSet =
HashSet<JS::Compartment*, DefaultHasher<JS::Compartment*>,
SystemAllocPolicy>;
class DebugAPI {
public:
friend class Debugger;
@ -103,6 +107,11 @@ class DebugAPI {
// Add sweep group edges due to the presence of any debuggers.
static MOZ_MUST_USE bool findSweepGroupEdges(JSRuntime* rt);
// Find the target compartments of cross compartment edges.
static bool findCrossCompartmentTargets(JSRuntime* rt,
JS::Compartment* source,
CompartmentSet& targets);
// Sweep breakpoints in a script associated with any debugger.
static inline void sweepBreakpoints(JSFreeOp* fop, JSScript* script);

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

@ -3565,6 +3565,59 @@ void DebugAPI::traceCrossCompartmentEdges(JSTracer* trc) {
}
}
/*
* Before performing a collection, the GC tries to find whether any collected
* compartments are expected to die. To do this is needs to know about any cross
* compartment pointers the debugger has that may keep compartments alive. This
* is done by calling findCrossCompartmentTargets for compartments it suspects
* are live.
*/
bool DebugAPI::findCrossCompartmentTargets(JSRuntime* rt,
JS::Compartment* source,
CompartmentSet& targets) {
for (Debugger* dbg : rt->debuggerList()) {
if (dbg->compartment() == source) {
if (!dbg->findCrossCompartmentTargets(targets)) {
return false;
}
}
}
return true;
}
bool Debugger::findCrossCompartmentTargets(CompartmentSet& targets) {
for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
Compartment* comp = e.front().unbarrieredGet()->compartment();
if (!targets.put(comp)) {
return false;
}
}
bool ok = true;
forEachWeakMap([&](auto& weakMap) {
if (ok && !weakMap.findCrossCompartmentTargets(targets)) {
ok = false;
}
});
return ok;
}
template <class Referent, class Wrapper, bool InvisibleKeysOk>
bool DebuggerWeakMap<Referent, Wrapper, InvisibleKeysOk>::
findCrossCompartmentTargets(CompartmentSet& targets) {
for (Enum e(*this); !e.empty(); e.popFront()) {
Key key = e.front().key();
JS::Compartment* comp = key->compartment();
if (!targets.put(comp)) {
return false;
}
}
return true;
}
#ifdef DEBUG
static bool RuntimeHasDebugger(JSRuntime* rt, Debugger* dbg) {

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

@ -310,10 +310,6 @@ class DebuggerWeakMap : private WeakMap<HeapPtr<Referent*>, HeapPtr<Wrapper*>> {
typedef HeapPtr<Referent*> Key;
typedef HeapPtr<Wrapper*> Value;
typedef HashMap<JS::Zone*, uintptr_t, DefaultHasher<JS::Zone*>,
ZoneAllocPolicy>
CountMap;
JS::Compartment* compartment;
public:
@ -375,6 +371,7 @@ class DebuggerWeakMap : private WeakMap<HeapPtr<Referent*>, HeapPtr<Wrapper*>> {
}
}
bool findCrossCompartmentTargets(CompartmentSet& targets);
bool findSweepGroupEdges(JS::Zone* debuggerZone);
private:
@ -832,6 +829,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger> {
void traceForMovingGC(JSTracer* trc);
void traceCrossCompartmentEdges(JSTracer* tracer);
bool findCrossCompartmentTargets(CompartmentSet& targets);
static const JSClassOps classOps_;
public:
@ -1051,6 +1050,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger> {
static Debugger* fromChildJSObject(JSObject* obj);
Zone* zone() const { return toJSObject()->zone(); }
JS::Compartment* compartment() const { return toJSObject()->compartment(); }
bool hasMemory() const;
DebuggerMemory& memory() const;

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

@ -4028,7 +4028,7 @@ bool GCRuntime::beginMarkPhase(JS::GCReason reason, AutoGCSession& session) {
}
if (isIncremental) {
markCompartments();
findDeadCompartments();
}
updateMemoryCountersOnGCStart();
@ -4046,7 +4046,7 @@ bool GCRuntime::beginMarkPhase(JS::GCReason reason, AutoGCSession& session) {
return true;
}
void GCRuntime::markCompartments() {
void GCRuntime::findDeadCompartments() {
gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::MARK_ROOTS);
gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::MARK_COMPARTMENTS);
@ -4056,8 +4056,8 @@ void GCRuntime::markCompartments() {
* flag is false. The maybeAlive flag is set if:
*
* (1) the compartment has been entered (set in beginMarkPhase() above)
* (2) the compartment is not being collected (set in beginMarkPhase()
* above)
* (2) the compartment's zone is not being collected (set in
* beginMarkPhase() above)
* (3) an object in the compartment was marked during root marking, either
* as a black root or a gray root (set in RootMarking.cpp), or
* (4) the compartment has incoming cross-compartment edges from another
@ -4080,7 +4080,7 @@ void GCRuntime::markCompartments() {
* allocation and read barriers during JS_TransplantObject and the like.
*/
/* Propagate the maybeAlive flag via cross-compartment edges. */
// Propagate the maybeAlive flag via cross-compartment edges.
Vector<Compartment*, 0, js::SystemAllocPolicy> workList;
@ -4094,6 +4094,8 @@ void GCRuntime::markCompartments() {
while (!workList.empty()) {
Compartment* comp = workList.popCopy();
// Check the cross compartment map.
for (Compartment::WrappedObjectCompartmentEnum e(comp); !e.empty();
e.popFront()) {
Compartment* dest = e.front();
@ -4104,9 +4106,24 @@ void GCRuntime::markCompartments() {
}
}
}
// Check debugger cross compartment edges.
CompartmentSet debuggerTargets;
if (!DebugAPI::findCrossCompartmentTargets(rt, comp, debuggerTargets)) {
return;
}
for (auto e = debuggerTargets.all(); !e.empty(); e.popFront()) {
Compartment* dest = e.front();
if (!dest->gcState.maybeAlive) {
dest->gcState.maybeAlive = true;
if (!workList.append(dest)) {
return;
}
}
}
}
/* Set scheduleForDestruction based on maybeAlive. */
// Set scheduledForDestruction based on maybeAlive.
for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) {
MOZ_ASSERT(!comp->gcState.scheduledForDestruction);

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

@ -620,7 +620,7 @@ class GCRuntime {
void traceEmbeddingGrayRoots(JSTracer* trc);
void checkNoRuntimeRoots(AutoGCSession& session);
void maybeDoCycleCollection();
void markCompartments();
void findDeadCompartments();
IncrementalProgress markUntilBudgetExhausted(SliceBudget& sliceBudget,
gcstats::PhaseKind phase);
void drainMarkStack();