mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 817343 - GC validation isn't happening r=billm
--HG-- rename : dom/tests/browser/browser_ConsoleStoragePBTest_perwindowpb.js => dom/tests/browser/browser_ConsoleStoragePBTest.js rename : toolkit/components/places/tests/browser/browser_visituri_privatebrowsing_perwindowpb.js => toolkit/components/places/tests/browser/browser_visituri_privatebrowsing.js extra : rebase_source : 7deb5ea796a0dfd68a3baa5b616af5ec98db545f
This commit is contained in:
parent
e3cd8d0be2
commit
853469c636
@ -807,6 +807,9 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
||||
gcSweepCompartment(NULL),
|
||||
gcSweepKindIndex(0),
|
||||
gcArenasAllocatedDuringSweep(NULL),
|
||||
#ifdef DEBUG
|
||||
gcMarkingValidator(NULL),
|
||||
#endif
|
||||
gcInterFrameGC(0),
|
||||
gcSliceBudget(SliceBudget::Unlimited),
|
||||
gcIncrementalEnabled(true),
|
||||
|
@ -450,6 +450,10 @@ class PerThreadData : public js::PerThreadDataFriendFields
|
||||
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
|
||||
};
|
||||
|
||||
namespace gc {
|
||||
class MarkingValidator;
|
||||
} // namespace gc
|
||||
|
||||
} // namespace js
|
||||
|
||||
struct JSRuntime : js::RuntimeFriendFields
|
||||
@ -729,6 +733,10 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
*/
|
||||
js::gc::ArenaHeader *gcArenasAllocatedDuringSweep;
|
||||
|
||||
#ifdef DEBUG
|
||||
js::gc::MarkingValidator *gcMarkingValidator;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Indicates that a GC slice has taken place in the middle of an animation
|
||||
* frame, rather than at the beginning. In this case, the next slice will be
|
||||
|
220
js/src/jsgc.cpp
220
js/src/jsgc.cpp
@ -2698,7 +2698,8 @@ BeginMarkPhase(JSRuntime *rt)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
template <class CompartmentIter>
|
||||
static void
|
||||
MarkWeakReferences(JSRuntime *rt, gcstats::Phase phase)
|
||||
{
|
||||
GCMarker *gcmarker = &rt->gcMarker;
|
||||
@ -2709,7 +2710,7 @@ MarkWeakReferences(JSRuntime *rt, gcstats::Phase phase)
|
||||
|
||||
for (;;) {
|
||||
bool markedAny = false;
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
for (CompartmentIter c(rt); !c.done(); c.next()) {
|
||||
markedAny |= WatchpointMap::markCompartmentIteratively(c, gcmarker);
|
||||
markedAny |= WeakMapBase::markCompartmentIteratively(c, gcmarker);
|
||||
}
|
||||
@ -2724,6 +2725,20 @@ MarkWeakReferences(JSRuntime *rt, gcstats::Phase phase)
|
||||
JS_ASSERT(gcmarker->isDrained());
|
||||
}
|
||||
|
||||
static void
|
||||
MarkWeakReferencesInCurrentGroup(JSRuntime *rt, gcstats::Phase phase)
|
||||
{
|
||||
MarkWeakReferences<GCCompartmentGroupIter>(rt, phase);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
MarkAllWeakReferences(JSRuntime *rt, gcstats::Phase phase)
|
||||
{
|
||||
MarkWeakReferences<GCCompartmentsIter>(rt, phase);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
MarkGrayReferences(JSRuntime *rt)
|
||||
{
|
||||
@ -2743,78 +2758,164 @@ MarkGrayReferences(JSRuntime *rt)
|
||||
gcmarker->drainMarkStack(budget);
|
||||
}
|
||||
|
||||
MarkWeakReferences(rt, gcstats::PHASE_SWEEP_MARK_GRAY_WEAK);
|
||||
MarkWeakReferencesInCurrentGroup(rt, gcstats::PHASE_SWEEP_MARK_GRAY_WEAK);
|
||||
|
||||
JS_ASSERT(gcmarker->isDrained());
|
||||
|
||||
gcmarker->setMarkColorBlack();
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
ValidateIncrementalMarking(JSRuntime *rt)
|
||||
|
||||
class js::gc::MarkingValidator
|
||||
{
|
||||
typedef HashMap<Chunk *, uintptr_t *, GCChunkHasher, SystemAllocPolicy> BitmapMap;
|
||||
public:
|
||||
MarkingValidator(JSRuntime *rt);
|
||||
~MarkingValidator();
|
||||
void nonIncrementalMark();
|
||||
void validate();
|
||||
|
||||
private:
|
||||
JSRuntime *runtime;
|
||||
bool initialized;
|
||||
|
||||
typedef HashMap<Chunk *, ChunkBitmap *, GCChunkHasher, SystemAllocPolicy> BitmapMap;
|
||||
BitmapMap map;
|
||||
};
|
||||
|
||||
js::gc::MarkingValidator::MarkingValidator(JSRuntime *rt)
|
||||
: runtime(rt),
|
||||
initialized(false)
|
||||
{}
|
||||
|
||||
js::gc::MarkingValidator::~MarkingValidator()
|
||||
{
|
||||
if (!map.initialized())
|
||||
return;
|
||||
|
||||
for (BitmapMap::Range r(map.all()); !r.empty(); r.popFront())
|
||||
js_delete(r.front().value);
|
||||
}
|
||||
|
||||
void
|
||||
js::gc::MarkingValidator::nonIncrementalMark()
|
||||
{
|
||||
/*
|
||||
* Perform a non-incremental mark for all collecting compartments and record
|
||||
* the results for later comparison.
|
||||
*
|
||||
* Currently this does not validate gray marking.
|
||||
*/
|
||||
|
||||
if (!map.init())
|
||||
return;
|
||||
|
||||
GCMarker *gcmarker = &rt->gcMarker;
|
||||
GCMarker *gcmarker = &runtime->gcMarker;
|
||||
|
||||
/* Save existing mark bits. */
|
||||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) {
|
||||
for (GCChunkSet::Range r(runtime->gcChunkSet.all()); !r.empty(); r.popFront()) {
|
||||
ChunkBitmap *bitmap = &r.front()->bitmap;
|
||||
uintptr_t *entry = (uintptr_t *)js_malloc(sizeof(bitmap->bitmap));
|
||||
ChunkBitmap *entry = js_new<ChunkBitmap>();
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
memcpy(entry, bitmap->bitmap, sizeof(bitmap->bitmap));
|
||||
memcpy(entry->bitmap, bitmap->bitmap, sizeof(bitmap->bitmap));
|
||||
if (!map.putNew(r.front(), entry))
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save and reset the lists of live weakmaps for the compartments we are collecting. */
|
||||
/*
|
||||
* Save the lists of live weakmaps and array buffers for the compartments we
|
||||
* are collecting.
|
||||
*/
|
||||
WeakMapVector weakmaps;
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
if (!WeakMapBase::saveCompartmentWeakMapList(c, weakmaps))
|
||||
ArrayBufferVector arrayBuffers;
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
|
||||
if (!WeakMapBase::saveCompartmentWeakMapList(c, weakmaps) ||
|
||||
!ArrayBufferObject::saveArrayBufferList(c, arrayBuffers))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
WeakMapBase::resetCompartmentWeakMapList(c);
|
||||
|
||||
/*
|
||||
* After this point, the function should run to completion, so we shouldn't
|
||||
* do anything fallible.
|
||||
*/
|
||||
initialized = true;
|
||||
|
||||
/*
|
||||
* Reset the lists of live weakmaps and array buffers for the compartments we
|
||||
* are collecting.
|
||||
*/
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
|
||||
WeakMapBase::resetCompartmentWeakMapList(c);
|
||||
ArrayBufferObject::resetArrayBufferList(c);
|
||||
}
|
||||
|
||||
/* Re-do all the marking, but non-incrementally. */
|
||||
js::gc::State state = rt->gcIncrementalState;
|
||||
rt->gcIncrementalState = MARK_ROOTS;
|
||||
js::gc::State state = runtime->gcIncrementalState;
|
||||
runtime->gcIncrementalState = MARK_ROOTS;
|
||||
|
||||
JS_ASSERT(gcmarker->isDrained());
|
||||
gcmarker->reset();
|
||||
|
||||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||
for (GCChunkSet::Range r(runtime->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||
r.front()->bitmap.clear();
|
||||
|
||||
MarkRuntime(gcmarker, true);
|
||||
{
|
||||
gcstats::AutoPhase ap1(runtime->gcStats, gcstats::PHASE_MARK);
|
||||
gcstats::AutoPhase ap2(runtime->gcStats, gcstats::PHASE_MARK_ROOTS);
|
||||
MarkRuntime(gcmarker, true);
|
||||
}
|
||||
|
||||
SliceBudget budget;
|
||||
rt->gcIncrementalState = MARK;
|
||||
rt->gcMarker.drainMarkStack(budget);
|
||||
MarkWeakReferences(rt, gcstats::PHASE_SWEEP_MARK_WEAK);
|
||||
MarkGrayReferences(rt);
|
||||
runtime->gcIncrementalState = MARK;
|
||||
runtime->gcMarker.drainMarkStack(budget);
|
||||
|
||||
/* Now verify that we have the same mark bits as before. */
|
||||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) {
|
||||
{
|
||||
gcstats::AutoPhase ap(runtime->gcStats, gcstats::PHASE_SWEEP);
|
||||
MarkAllWeakReferences(runtime, gcstats::PHASE_SWEEP_MARK_WEAK);
|
||||
}
|
||||
|
||||
/* Take a copy of the non-incremental mark state and restore the original. */
|
||||
for (GCChunkSet::Range r(runtime->gcChunkSet.all()); !r.empty(); r.popFront()) {
|
||||
Chunk *chunk = r.front();
|
||||
ChunkBitmap *bitmap = &chunk->bitmap;
|
||||
uintptr_t *entry = map.lookup(r.front())->value;
|
||||
ChunkBitmap incBitmap;
|
||||
ChunkBitmap *entry = map.lookup(chunk)->value;
|
||||
js::Swap(*entry, *bitmap);
|
||||
}
|
||||
|
||||
memcpy(incBitmap.bitmap, entry, sizeof(incBitmap.bitmap));
|
||||
js_free(entry);
|
||||
/* Restore the weak map and array buffer lists. */
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
|
||||
WeakMapBase::resetCompartmentWeakMapList(c);
|
||||
ArrayBufferObject::resetArrayBufferList(c);
|
||||
}
|
||||
WeakMapBase::restoreCompartmentWeakMapLists(weakmaps);
|
||||
ArrayBufferObject::restoreArrayBufferLists(arrayBuffers);
|
||||
|
||||
runtime->gcIncrementalState = state;
|
||||
}
|
||||
|
||||
void
|
||||
js::gc::MarkingValidator::validate()
|
||||
{
|
||||
/*
|
||||
* Validates the incremental marking for a single compartment by comparing
|
||||
* the mark bits to those previously recorded for a non-incremental mark.
|
||||
*/
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
for (GCChunkSet::Range r(runtime->gcChunkSet.all()); !r.empty(); r.popFront()) {
|
||||
Chunk *chunk = r.front();
|
||||
BitmapMap::Ptr ptr = map.lookup(chunk);
|
||||
if (!ptr)
|
||||
continue; /* Allocated after we did the non-incremental mark. */
|
||||
|
||||
ChunkBitmap *bitmap = ptr->value;
|
||||
ChunkBitmap *incBitmap = &chunk->bitmap;
|
||||
|
||||
for (size_t i = 0; i < ArenasPerChunk; i++) {
|
||||
if (chunk->decommittedArenas.get(i))
|
||||
@ -2822,7 +2923,7 @@ ValidateIncrementalMarking(JSRuntime *rt)
|
||||
Arena *arena = &chunk->arenas[i];
|
||||
if (!arena->aheader.allocated())
|
||||
continue;
|
||||
if (!arena->aheader.compartment->isCollecting())
|
||||
if (!arena->aheader.compartment->isGCSweeping())
|
||||
continue;
|
||||
if (arena->aheader.allocatedDuringIncremental)
|
||||
continue;
|
||||
@ -2837,31 +2938,45 @@ ValidateIncrementalMarking(JSRuntime *rt)
|
||||
* If a non-incremental GC wouldn't have collected a cell, then
|
||||
* an incremental GC won't collect it.
|
||||
*/
|
||||
JS_ASSERT_IF(bitmap->isMarked(cell, BLACK), incBitmap.isMarked(cell, BLACK));
|
||||
|
||||
/*
|
||||
* If the cycle collector isn't allowed to collect an object
|
||||
* after a non-incremental GC has run, then it isn't allowed to
|
||||
* collected it after an incremental GC.
|
||||
*/
|
||||
JS_ASSERT_IF(!bitmap->isMarked(cell, GRAY), !incBitmap.isMarked(cell, GRAY));
|
||||
JS_ASSERT_IF(bitmap->isMarked(cell, BLACK), incBitmap->isMarked(cell, BLACK));
|
||||
|
||||
thing += Arena::thingSize(kind);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(bitmap->bitmap, incBitmap.bitmap, sizeof(incBitmap.bitmap));
|
||||
}
|
||||
|
||||
/* Restore the weak map lists. */
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
WeakMapBase::resetCompartmentWeakMapList(c);
|
||||
WeakMapBase::restoreCompartmentWeakMapLists(weakmaps);
|
||||
|
||||
rt->gcIncrementalState = state;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
ComputeNonIncrementalMarkingForValidation(JSRuntime *rt)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(!rt->gcMarkingValidator);
|
||||
if (rt->gcIsIncremental && rt->gcValidate)
|
||||
rt->gcMarkingValidator = js_new<MarkingValidator>(rt);
|
||||
if (rt->gcMarkingValidator)
|
||||
rt->gcMarkingValidator->nonIncrementalMark();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
ValidateIncrementalMarking(JSRuntime *rt)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (rt->gcMarkingValidator)
|
||||
rt->gcMarkingValidator->validate();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
FinishMarkingValidation(JSRuntime *rt)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
js_delete(rt->gcMarkingValidator);
|
||||
rt->gcMarkingValidator = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
DropStringWrappers(JSRuntime *rt)
|
||||
@ -3206,7 +3321,7 @@ EndMarkingCompartmentGroup(JSRuntime *rt)
|
||||
*/
|
||||
MarkIncomingCrossCompartmentPointers(rt, BLACK);
|
||||
|
||||
MarkWeakReferences(rt, gcstats::PHASE_SWEEP_MARK_WEAK);
|
||||
MarkWeakReferencesInCurrentGroup(rt, gcstats::PHASE_SWEEP_MARK_WEAK);
|
||||
|
||||
/*
|
||||
* Change state of current group to MarkGray to restrict marking to this
|
||||
@ -3257,6 +3372,8 @@ BeginSweepingCompartmentGroup(JSRuntime *rt)
|
||||
sweepingAtoms = true;
|
||||
}
|
||||
|
||||
ValidateIncrementalMarking(rt);
|
||||
|
||||
FreeOp fop(rt, rt->gcSweepOnBackgroundThread);
|
||||
|
||||
{
|
||||
@ -3358,6 +3475,9 @@ BeginSweepPhase(JSRuntime *rt)
|
||||
* true so that any attempt to allocate a GC-thing from a finalizer will
|
||||
* fail, rather than nest badly and leave the unmarked newborn to be swept.
|
||||
*/
|
||||
|
||||
ComputeNonIncrementalMarkingForValidation(rt);
|
||||
|
||||
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
@ -3567,6 +3687,8 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
|
||||
#endif
|
||||
}
|
||||
|
||||
FinishMarkingValidation(rt);
|
||||
|
||||
rt->gcLastGCTime = PRMJ_Now();
|
||||
}
|
||||
|
||||
|
@ -594,11 +594,11 @@ ArrayBufferObject::sweep(JSCompartment *compartment)
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::resetArrayBufferList(JSCompartment *compartment)
|
||||
ArrayBufferObject::resetArrayBufferList(JSCompartment *comp)
|
||||
{
|
||||
JSObject *buffer = compartment->gcLiveArrayBuffers;
|
||||
JSObject *buffer = comp->gcLiveArrayBuffers;
|
||||
JS_ASSERT(buffer != UNSET_BUFFER_LINK);
|
||||
compartment->gcLiveArrayBuffers = NULL;
|
||||
comp->gcLiveArrayBuffers = NULL;
|
||||
|
||||
while (buffer) {
|
||||
JSObject *view = *GetViewList(&buffer->asArrayBuffer());
|
||||
@ -612,6 +612,38 @@ ArrayBufferObject::resetArrayBufferList(JSCompartment *compartment)
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector)
|
||||
{
|
||||
JSObject *obj = comp->gcLiveArrayBuffers;
|
||||
while (obj) {
|
||||
JS_ASSERT(obj != UNSET_BUFFER_LINK);
|
||||
ArrayBufferObject *buffer = &obj->asArrayBuffer();
|
||||
if (!vector.append(buffer))
|
||||
return false;
|
||||
|
||||
JSObject *view = *GetViewList(buffer);
|
||||
JS_ASSERT(view);
|
||||
obj = BufferLink(view);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector)
|
||||
{
|
||||
for (ArrayBufferObject **p = vector.begin(); p != vector.end(); p++) {
|
||||
ArrayBufferObject *buffer = *p;
|
||||
JSCompartment *comp = buffer->compartment();
|
||||
JSObject *firstView = *GetViewList(&buffer->asArrayBuffer());
|
||||
JS_ASSERT(firstView);
|
||||
JS_ASSERT(firstView->compartment() == comp);
|
||||
JS_ASSERT(BufferLink(firstView) == UNSET_BUFFER_LINK);
|
||||
SetBufferLink(firstView, comp->gcLiveArrayBuffers);
|
||||
comp->gcLiveArrayBuffers = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
JSBool
|
||||
ArrayBufferObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleObject objp, MutableHandleShape propp)
|
||||
|
@ -17,6 +17,8 @@ typedef struct JSProperty JSProperty;
|
||||
|
||||
namespace js {
|
||||
|
||||
typedef Vector<ArrayBufferObject *, 0, SystemAllocPolicy> ArrayBufferVector;
|
||||
|
||||
/*
|
||||
* ArrayBufferObject
|
||||
*
|
||||
@ -133,6 +135,8 @@ class ArrayBufferObject : public JSObject
|
||||
static void sweep(JSCompartment *rt);
|
||||
|
||||
static void resetArrayBufferList(JSCompartment *rt);
|
||||
static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector);
|
||||
static void restoreArrayBufferLists(ArrayBufferVector &vector);
|
||||
|
||||
static bool stealContents(JSContext *cx, JSObject *obj, void **contents,
|
||||
uint8_t **data);
|
||||
|
Loading…
Reference in New Issue
Block a user