diff options
Diffstat (limited to 'js/src/gc/Verifier.cpp')
-rw-r--r-- | js/src/gc/Verifier.cpp | 414 |
1 files changed, 0 insertions, 414 deletions
diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index dd4031606..3ebbbb4f6 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -26,420 +26,6 @@ using namespace js; using namespace js::gc; -#ifdef JS_GC_ZEAL - -/* - * Write barrier verification - * - * The next few functions are for write barrier verification. - * - * The VerifyBarriers function is a shorthand. It checks if a verification phase - * is currently running. If not, it starts one. Otherwise, it ends the current - * phase and starts a new one. - * - * The user can adjust the frequency of verifications, which causes - * VerifyBarriers to be a no-op all but one out of N calls. However, if the - * |always| parameter is true, it starts a new phase no matter what. - * - * Pre-Barrier Verifier: - * When StartVerifyBarriers is called, a snapshot is taken of all objects in - * the GC heap and saved in an explicit graph data structure. Later, - * EndVerifyBarriers traverses the heap again. Any pointer values that were in - * the snapshot and are no longer found must be marked; otherwise an assertion - * triggers. Note that we must not GC in between starting and finishing a - * verification phase. - */ - -struct EdgeValue -{ - void* thing; - JS::TraceKind kind; - const char* label; -}; - -struct VerifyNode -{ - void* thing; - JS::TraceKind kind; - uint32_t count; - EdgeValue edges[1]; -}; - -typedef HashMap<void*, VerifyNode*, DefaultHasher<void*>, SystemAllocPolicy> NodeMap; - -/* - * The verifier data structures are simple. The entire graph is stored in a - * single block of memory. At the beginning is a VerifyNode for the root - * node. It is followed by a sequence of EdgeValues--the exact number is given - * in the node. After the edges come more nodes and their edges. - * - * The edgeptr and term fields are used to allocate out of the block of memory - * for the graph. If we run out of memory (i.e., if edgeptr goes beyond term), - * we just abandon the verification. - * - * The nodemap field is a hashtable that maps from the address of the GC thing - * to the VerifyNode that represents it. - */ -class js::VerifyPreTracer final : public JS::CallbackTracer -{ - JS::AutoDisableGenerationalGC noggc; - - void onChild(const JS::GCCellPtr& thing) override; - - public: - /* The gcNumber when the verification began. */ - uint64_t number; - - /* This counts up to gcZealFrequency to decide whether to verify. */ - int count; - - /* This graph represents the initial GC "snapshot". */ - VerifyNode* curnode; - VerifyNode* root; - char* edgeptr; - char* term; - NodeMap nodemap; - - explicit VerifyPreTracer(JSRuntime* rt) - : JS::CallbackTracer(rt), noggc(rt), number(rt->gc.gcNumber()), count(0), curnode(nullptr), - root(nullptr), edgeptr(nullptr), term(nullptr) - {} - - ~VerifyPreTracer() { - js_free(root); - } -}; - -/* - * This function builds up the heap snapshot by adding edges to the current - * node. - */ -void -VerifyPreTracer::onChild(const JS::GCCellPtr& thing) -{ - MOZ_ASSERT(!IsInsideNursery(thing.asCell())); - - // Skip things in other runtimes. - if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime()) - return; - - edgeptr += sizeof(EdgeValue); - if (edgeptr >= term) { - edgeptr = term; - return; - } - - VerifyNode* node = curnode; - uint32_t i = node->count; - - node->edges[i].thing = thing.asCell(); - node->edges[i].kind = thing.kind(); - node->edges[i].label = contextName(); - node->count++; -} - -static VerifyNode* -MakeNode(VerifyPreTracer* trc, void* thing, JS::TraceKind kind) -{ - NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing); - if (!p) { - VerifyNode* node = (VerifyNode*)trc->edgeptr; - trc->edgeptr += sizeof(VerifyNode) - sizeof(EdgeValue); - if (trc->edgeptr >= trc->term) { - trc->edgeptr = trc->term; - return nullptr; - } - - node->thing = thing; - node->count = 0; - node->kind = kind; - if (!trc->nodemap.add(p, thing, node)) { - trc->edgeptr = trc->term; - return nullptr; - } - - return node; - } - return nullptr; -} - -static VerifyNode* -NextNode(VerifyNode* node) -{ - if (node->count == 0) - return (VerifyNode*)((char*)node + sizeof(VerifyNode) - sizeof(EdgeValue)); - else - return (VerifyNode*)((char*)node + sizeof(VerifyNode) + - sizeof(EdgeValue)*(node->count - 1)); -} - -void -gc::GCRuntime::startVerifyPreBarriers() -{ - if (verifyPreData || isIncrementalGCInProgress()) - return; - - if (IsIncrementalGCUnsafe(rt) != AbortReason::None) - return; - - number++; - - VerifyPreTracer* trc = js_new<VerifyPreTracer>(rt); - if (!trc) - return; - - AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms); - - for (auto chunk = allNonEmptyChunks(); !chunk.done(); chunk.next()) - chunk->bitmap.clear(); - - gcstats::AutoPhase ap(stats, gcstats::PHASE_TRACE_HEAP); - - const size_t size = 64 * 1024 * 1024; - trc->root = (VerifyNode*)js_malloc(size); - if (!trc->root) - goto oom; - trc->edgeptr = (char*)trc->root; - trc->term = trc->edgeptr + size; - - if (!trc->nodemap.init()) - goto oom; - - /* Create the root node. */ - trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0)); - - incrementalState = State::MarkRoots; - - /* Make all the roots be edges emanating from the root node. */ - traceRuntime(trc, prep.session().lock); - - VerifyNode* node; - node = trc->curnode; - if (trc->edgeptr == trc->term) - goto oom; - - /* For each edge, make a node for it if one doesn't already exist. */ - while ((char*)node < trc->edgeptr) { - for (uint32_t i = 0; i < node->count; i++) { - EdgeValue& e = node->edges[i]; - VerifyNode* child = MakeNode(trc, e.thing, e.kind); - if (child) { - trc->curnode = child; - js::TraceChildren(trc, e.thing, e.kind); - } - if (trc->edgeptr == trc->term) - goto oom; - } - - node = NextNode(node); - } - - verifyPreData = trc; - incrementalState = State::Mark; - marker.start(); - - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { - PurgeJITCaches(zone); - if (!zone->usedByExclusiveThread) { - zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit); - zone->arenas.purge(); - } - } - - return; - -oom: - incrementalState = State::NotActive; - js_delete(trc); - verifyPreData = nullptr; -} - -static bool -IsMarkedOrAllocated(TenuredCell* cell) -{ - return cell->isMarked() || cell->arena()->allocatedDuringIncremental; -} - -struct CheckEdgeTracer : public JS::CallbackTracer { - VerifyNode* node; - explicit CheckEdgeTracer(JSRuntime* rt) : JS::CallbackTracer(rt), node(nullptr) {} - void onChild(const JS::GCCellPtr& thing) override; -}; - -static const uint32_t MAX_VERIFIER_EDGES = 1000; - -/* - * This function is called by EndVerifyBarriers for every heap edge. If the edge - * already existed in the original snapshot, we "cancel it out" by overwriting - * it with nullptr. EndVerifyBarriers later asserts that the remaining - * non-nullptr edges (i.e., the ones from the original snapshot that must have - * been modified) must point to marked objects. - */ -void -CheckEdgeTracer::onChild(const JS::GCCellPtr& thing) -{ - // Skip things in other runtimes. - if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime()) - return; - - /* Avoid n^2 behavior. */ - if (node->count > MAX_VERIFIER_EDGES) - return; - - for (uint32_t i = 0; i < node->count; i++) { - if (node->edges[i].thing == thing.asCell()) { - MOZ_ASSERT(node->edges[i].kind == thing.kind()); - node->edges[i].thing = nullptr; - return; - } - } -} - -void -js::gc::AssertSafeToSkipBarrier(TenuredCell* thing) -{ - Zone* zone = thing->zoneFromAnyThread(); - MOZ_ASSERT(!zone->needsIncrementalBarrier() || zone->isAtomsZone()); -} - -static bool -IsMarkedOrAllocated(const EdgeValue& edge) -{ - if (!edge.thing || IsMarkedOrAllocated(TenuredCell::fromPointer(edge.thing))) - return true; - - // Permanent atoms and well-known symbols aren't marked during graph traversal. - if (edge.kind == JS::TraceKind::String && static_cast<JSString*>(edge.thing)->isPermanentAtom()) - return true; - if (edge.kind == JS::TraceKind::Symbol && static_cast<JS::Symbol*>(edge.thing)->isWellKnownSymbol()) - return true; - - return false; -} - -void -gc::GCRuntime::endVerifyPreBarriers() -{ - VerifyPreTracer* trc = verifyPreData; - - if (!trc) - return; - - MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt)); - - AutoPrepareForTracing prep(rt->contextFromMainThread(), SkipAtoms); - - bool compartmentCreated = false; - - /* We need to disable barriers before tracing, which may invoke barriers. */ - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { - if (!zone->needsIncrementalBarrier()) - compartmentCreated = true; - - zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit); - PurgeJITCaches(zone); - } - - /* - * We need to bump gcNumber so that the methodjit knows that jitcode has - * been discarded. - */ - MOZ_ASSERT(trc->number == number); - number++; - - verifyPreData = nullptr; - incrementalState = State::NotActive; - - if (!compartmentCreated && IsIncrementalGCUnsafe(rt) == AbortReason::None) { - CheckEdgeTracer cetrc(rt); - - /* Start after the roots. */ - VerifyNode* node = NextNode(trc->root); - while ((char*)node < trc->edgeptr) { - cetrc.node = node; - js::TraceChildren(&cetrc, node->thing, node->kind); - - if (node->count <= MAX_VERIFIER_EDGES) { - for (uint32_t i = 0; i < node->count; i++) { - EdgeValue& edge = node->edges[i]; - if (!IsMarkedOrAllocated(edge)) { - char msgbuf[1024]; - SprintfLiteral(msgbuf, - "[barrier verifier] Unmarked edge: %s %p '%s' edge to %s %p", - JS::GCTraceKindToAscii(node->kind), node->thing, - edge.label, - JS::GCTraceKindToAscii(edge.kind), edge.thing); - MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__); - MOZ_CRASH(); - } - } - } - - node = NextNode(node); - } - } - - marker.reset(); - marker.stop(); - - js_delete(trc); -} - -/*** Barrier Verifier Scheduling ***/ - -void -gc::GCRuntime::verifyPreBarriers() -{ - if (verifyPreData) - endVerifyPreBarriers(); - else - startVerifyPreBarriers(); -} - -void -gc::VerifyBarriers(JSRuntime* rt, VerifierType type) -{ - if (type == PreBarrierVerifier) - rt->gc.verifyPreBarriers(); -} - -void -gc::GCRuntime::maybeVerifyPreBarriers(bool always) -{ - if (!hasZealMode(ZealMode::VerifierPre)) - return; - - if (rt->mainThread.suppressGC) - return; - - if (verifyPreData) { - if (++verifyPreData->count < zealFrequency && !always) - return; - - endVerifyPreBarriers(); - } - - startVerifyPreBarriers(); -} - -void -js::gc::MaybeVerifyBarriers(JSContext* cx, bool always) -{ - GCRuntime* gc = &cx->runtime()->gc; - gc->maybeVerifyPreBarriers(always); -} - -void -js::gc::GCRuntime::finishVerifier() -{ - if (verifyPreData) { - js_delete(verifyPreData); - verifyPreData = nullptr; - } -} - -#endif /* JS_GC_ZEAL */ - #ifdef JSGC_HASH_TABLE_CHECKS class CheckHeapTracer : public JS::CallbackTracer |