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:
Jon Coppeard 2012-12-05 17:55:26 +00:00
parent e3cd8d0be2
commit 853469c636
5 changed files with 221 additions and 52 deletions

View File

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

View File

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

View File

@ -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();
}

View File

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

View File

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