LCOV - code coverage report
Current view: top level - js/src/gc - Verifier.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 5 274 1.8 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/DebugOnly.h"
       8             : #include "mozilla/IntegerPrintfMacros.h"
       9             : #include "mozilla/Sprintf.h"
      10             : 
      11             : #ifdef MOZ_VALGRIND
      12             : # include <valgrind/memcheck.h>
      13             : #endif
      14             : 
      15             : #include "gc/GCInternals.h"
      16             : #include "gc/PublicIterators.h"
      17             : #include "gc/Zone.h"
      18             : #include "js/HashTable.h"
      19             : #include "vm/JSContext.h"
      20             : 
      21             : #include "gc/ArenaList-inl.h"
      22             : #include "gc/GC-inl.h"
      23             : #include "gc/Marking-inl.h"
      24             : #include "vm/JSContext-inl.h"
      25             : 
      26             : using namespace js;
      27             : using namespace js::gc;
      28             : 
      29             : #ifdef JS_GC_ZEAL
      30             : 
      31             : /*
      32             :  * Write barrier verification
      33             :  *
      34             :  * The next few functions are for write barrier verification.
      35             :  *
      36             :  * The VerifyBarriers function is a shorthand. It checks if a verification phase
      37             :  * is currently running. If not, it starts one. Otherwise, it ends the current
      38             :  * phase and starts a new one.
      39             :  *
      40             :  * The user can adjust the frequency of verifications, which causes
      41             :  * VerifyBarriers to be a no-op all but one out of N calls. However, if the
      42             :  * |always| parameter is true, it starts a new phase no matter what.
      43             :  *
      44             :  * Pre-Barrier Verifier:
      45             :  *   When StartVerifyBarriers is called, a snapshot is taken of all objects in
      46             :  *   the GC heap and saved in an explicit graph data structure. Later,
      47             :  *   EndVerifyBarriers traverses the heap again. Any pointer values that were in
      48             :  *   the snapshot and are no longer found must be marked; otherwise an assertion
      49             :  *   triggers. Note that we must not GC in between starting and finishing a
      50             :  *   verification phase.
      51             :  */
      52             : 
      53             : struct EdgeValue
      54             : {
      55             :     void* thing;
      56             :     JS::TraceKind kind;
      57             :     const char* label;
      58             : };
      59             : 
      60             : struct VerifyNode
      61             : {
      62             :     void* thing;
      63             :     JS::TraceKind kind;
      64             :     uint32_t count;
      65             :     EdgeValue edges[1];
      66             : };
      67             : 
      68             : typedef HashMap<void*, VerifyNode*, DefaultHasher<void*>, SystemAllocPolicy> NodeMap;
      69             : 
      70             : /*
      71             :  * The verifier data structures are simple. The entire graph is stored in a
      72             :  * single block of memory. At the beginning is a VerifyNode for the root
      73             :  * node. It is followed by a sequence of EdgeValues--the exact number is given
      74             :  * in the node. After the edges come more nodes and their edges.
      75             :  *
      76             :  * The edgeptr and term fields are used to allocate out of the block of memory
      77             :  * for the graph. If we run out of memory (i.e., if edgeptr goes beyond term),
      78             :  * we just abandon the verification.
      79             :  *
      80             :  * The nodemap field is a hashtable that maps from the address of the GC thing
      81             :  * to the VerifyNode that represents it.
      82             :  */
      83             : class js::VerifyPreTracer final : public JS::CallbackTracer
      84             : {
      85             :     JS::AutoDisableGenerationalGC noggc;
      86             : 
      87             :     void onChild(const JS::GCCellPtr& thing) override;
      88             : 
      89             :   public:
      90             :     /* The gcNumber when the verification began. */
      91             :     uint64_t number;
      92             : 
      93             :     /* This counts up to gcZealFrequency to decide whether to verify. */
      94             :     int count;
      95             : 
      96             :     /* This graph represents the initial GC "snapshot". */
      97             :     VerifyNode* curnode;
      98             :     VerifyNode* root;
      99             :     char* edgeptr;
     100             :     char* term;
     101             :     NodeMap nodemap;
     102             : 
     103           0 :     explicit VerifyPreTracer(JSRuntime* rt)
     104           0 :       : JS::CallbackTracer(rt), noggc(rt->mainContextFromOwnThread()), number(rt->gc.gcNumber()),
     105           0 :         count(0), curnode(nullptr), root(nullptr), edgeptr(nullptr), term(nullptr)
     106           0 :     {}
     107             : 
     108           0 :     ~VerifyPreTracer() {
     109           0 :         js_free(root);
     110           0 :     }
     111             : };
     112             : 
     113             : /*
     114             :  * This function builds up the heap snapshot by adding edges to the current
     115             :  * node.
     116             :  */
     117             : void
     118           0 : VerifyPreTracer::onChild(const JS::GCCellPtr& thing)
     119             : {
     120           0 :     MOZ_ASSERT(!IsInsideNursery(thing.asCell()));
     121             : 
     122             :     // Skip things in other runtimes.
     123           0 :     if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime())
     124             :         return;
     125             : 
     126           0 :     edgeptr += sizeof(EdgeValue);
     127           0 :     if (edgeptr >= term) {
     128           0 :         edgeptr = term;
     129           0 :         return;
     130             :     }
     131             : 
     132           0 :     VerifyNode* node = curnode;
     133           0 :     uint32_t i = node->count;
     134             : 
     135           0 :     node->edges[i].thing = thing.asCell();
     136           0 :     node->edges[i].kind = thing.kind();
     137           0 :     node->edges[i].label = contextName();
     138           0 :     node->count++;
     139             : }
     140             : 
     141             : static VerifyNode*
     142           0 : MakeNode(VerifyPreTracer* trc, void* thing, JS::TraceKind kind)
     143             : {
     144           0 :     NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing);
     145           0 :     if (!p) {
     146           0 :         VerifyNode* node = (VerifyNode*)trc->edgeptr;
     147           0 :         trc->edgeptr += sizeof(VerifyNode) - sizeof(EdgeValue);
     148           0 :         if (trc->edgeptr >= trc->term) {
     149           0 :             trc->edgeptr = trc->term;
     150           0 :             return nullptr;
     151             :         }
     152             : 
     153           0 :         node->thing = thing;
     154           0 :         node->count = 0;
     155           0 :         node->kind = kind;
     156           0 :         if (!trc->nodemap.add(p, thing, node)) {
     157           0 :             trc->edgeptr = trc->term;
     158           0 :             return nullptr;
     159             :         }
     160             : 
     161           0 :         return node;
     162             :     }
     163             :     return nullptr;
     164             : }
     165             : 
     166             : static VerifyNode*
     167             : NextNode(VerifyNode* node)
     168             : {
     169           0 :     if (node->count == 0)
     170           0 :         return (VerifyNode*)((char*)node + sizeof(VerifyNode) - sizeof(EdgeValue));
     171             :     else
     172             :         return (VerifyNode*)((char*)node + sizeof(VerifyNode) +
     173           0 :                              sizeof(EdgeValue)*(node->count - 1));
     174             : }
     175             : 
     176             : void
     177           0 : gc::GCRuntime::startVerifyPreBarriers()
     178             : {
     179           0 :     if (verifyPreData || isIncrementalGCInProgress())
     180           0 :         return;
     181             : 
     182           0 :     JSContext* cx = rt->mainContextFromOwnThread();
     183           0 :     if (temporaryAbortIfWasmGc(cx))
     184             :         return;
     185             : 
     186           0 :     if (IsIncrementalGCUnsafe(rt) != AbortReason::None ||
     187           0 :         rt->hasHelperThreadZones())
     188             :     {
     189             :         return;
     190             :     }
     191             : 
     192           0 :     number++;
     193             : 
     194           0 :     VerifyPreTracer* trc = js_new<VerifyPreTracer>(rt);
     195           0 :     if (!trc)
     196             :         return;
     197             : 
     198           0 :     AutoPrepareForTracing prep(cx);
     199             : 
     200             :     {
     201           0 :         AutoLockGC lock(cx->runtime());
     202           0 :         for (auto chunk = allNonEmptyChunks(lock); !chunk.done(); chunk.next())
     203           0 :             chunk->bitmap.clear();
     204             :     }
     205             : 
     206           0 :     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::TRACE_HEAP);
     207             : 
     208           0 :     const size_t size = 64 * 1024 * 1024;
     209           0 :     trc->root = (VerifyNode*)js_malloc(size);
     210           0 :     if (!trc->root)
     211             :         goto oom;
     212           0 :     trc->edgeptr = (char*)trc->root;
     213           0 :     trc->term = trc->edgeptr + size;
     214             : 
     215           0 :     if (!trc->nodemap.init())
     216             :         goto oom;
     217             : 
     218             :     /* Create the root node. */
     219           0 :     trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
     220             : 
     221           0 :     incrementalState = State::MarkRoots;
     222             : 
     223             :     /* Make all the roots be edges emanating from the root node. */
     224           0 :     traceRuntime(trc, prep.session());
     225             : 
     226             :     VerifyNode* node;
     227           0 :     node = trc->curnode;
     228           0 :     if (trc->edgeptr == trc->term)
     229             :         goto oom;
     230             : 
     231             :     /* For each edge, make a node for it if one doesn't already exist. */
     232           0 :     while ((char*)node < trc->edgeptr) {
     233           0 :         for (uint32_t i = 0; i < node->count; i++) {
     234           0 :             EdgeValue& e = node->edges[i];
     235           0 :             VerifyNode* child = MakeNode(trc, e.thing, e.kind);
     236           0 :             if (child) {
     237           0 :                 trc->curnode = child;
     238           0 :                 js::TraceChildren(trc, e.thing, e.kind);
     239             :             }
     240           0 :             if (trc->edgeptr == trc->term)
     241             :                 goto oom;
     242             :         }
     243             : 
     244             :         node = NextNode(node);
     245             :     }
     246             : 
     247           0 :     verifyPreData = trc;
     248           0 :     incrementalState = State::Mark;
     249           0 :     marker.start();
     250             : 
     251           0 :     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
     252           0 :         MOZ_ASSERT(!zone->usedByHelperThread());
     253           0 :         zone->setNeedsIncrementalBarrier(true);
     254           0 :         zone->arenas.clearFreeLists();
     255             :     }
     256             : 
     257           0 :     return;
     258             : 
     259             : oom:
     260           0 :     incrementalState = State::NotActive;
     261           0 :     js_delete(trc);
     262           0 :     verifyPreData = nullptr;
     263             : }
     264             : 
     265             : static bool
     266             : IsMarkedOrAllocated(TenuredCell* cell)
     267             : {
     268           0 :     return cell->isMarkedAny();
     269             : }
     270             : 
     271             : struct CheckEdgeTracer : public JS::CallbackTracer {
     272             :     VerifyNode* node;
     273           0 :     explicit CheckEdgeTracer(JSRuntime* rt) : JS::CallbackTracer(rt), node(nullptr) {}
     274             :     void onChild(const JS::GCCellPtr& thing) override;
     275             : };
     276             : 
     277             : static const uint32_t MAX_VERIFIER_EDGES = 1000;
     278             : 
     279             : /*
     280             :  * This function is called by EndVerifyBarriers for every heap edge. If the edge
     281             :  * already existed in the original snapshot, we "cancel it out" by overwriting
     282             :  * it with nullptr. EndVerifyBarriers later asserts that the remaining
     283             :  * non-nullptr edges (i.e., the ones from the original snapshot that must have
     284             :  * been modified) must point to marked objects.
     285             :  */
     286             : void
     287           0 : CheckEdgeTracer::onChild(const JS::GCCellPtr& thing)
     288             : {
     289             :     // Skip things in other runtimes.
     290           0 :     if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime())
     291             :         return;
     292             : 
     293             :     /* Avoid n^2 behavior. */
     294           0 :     if (node->count > MAX_VERIFIER_EDGES)
     295             :         return;
     296             : 
     297           0 :     for (uint32_t i = 0; i < node->count; i++) {
     298           0 :         if (node->edges[i].thing == thing.asCell()) {
     299           0 :             MOZ_ASSERT(node->edges[i].kind == thing.kind());
     300           0 :             node->edges[i].thing = nullptr;
     301           0 :             return;
     302             :         }
     303             :     }
     304             : }
     305             : 
     306             : void
     307         643 : js::gc::AssertSafeToSkipBarrier(TenuredCell* thing)
     308             : {
     309        1929 :     mozilla::DebugOnly<Zone*> zone = thing->zoneFromAnyThread();
     310           0 :     MOZ_ASSERT(!zone->needsIncrementalBarrier() || zone->isAtomsZone());
     311           0 : }
     312             : 
     313             : static bool
     314           0 : IsMarkedOrAllocated(const EdgeValue& edge)
     315             : {
     316           0 :     if (!edge.thing || IsMarkedOrAllocated(TenuredCell::fromPointer(edge.thing)))
     317             :         return true;
     318             : 
     319             :     // Permanent atoms and well-known symbols aren't marked during graph traversal.
     320           0 :     if (edge.kind == JS::TraceKind::String && static_cast<JSString*>(edge.thing)->isPermanentAtom())
     321             :         return true;
     322           0 :     if (edge.kind == JS::TraceKind::Symbol && static_cast<JS::Symbol*>(edge.thing)->isWellKnownSymbol())
     323             :         return true;
     324             : 
     325             :     return false;
     326             : }
     327             : 
     328             : void
     329           0 : gc::GCRuntime::endVerifyPreBarriers()
     330             : {
     331           0 :     VerifyPreTracer* trc = verifyPreData;
     332             : 
     333           0 :     if (!trc)
     334           0 :         return;
     335             : 
     336           0 :     MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt));
     337             : 
     338           0 :     AutoPrepareForTracing prep(rt->mainContextFromOwnThread());
     339             : 
     340           0 :     bool compartmentCreated = false;
     341             : 
     342             :     /* We need to disable barriers before tracing, which may invoke barriers. */
     343           0 :     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
     344           0 :         if (!zone->needsIncrementalBarrier())
     345           0 :             compartmentCreated = true;
     346             : 
     347           0 :         zone->setNeedsIncrementalBarrier(false);
     348             :     }
     349             : 
     350             :     /*
     351             :      * We need to bump gcNumber so that the methodjit knows that jitcode has
     352             :      * been discarded.
     353             :      */
     354           0 :     MOZ_ASSERT(trc->number == number);
     355           0 :     number++;
     356             : 
     357           0 :     verifyPreData = nullptr;
     358           0 :     incrementalState = State::NotActive;
     359             : 
     360           0 :     if (!compartmentCreated &&
     361           0 :         IsIncrementalGCUnsafe(rt) == AbortReason::None &&
     362           0 :         !rt->hasHelperThreadZones())
     363             :     {
     364           0 :         CheckEdgeTracer cetrc(rt);
     365             : 
     366             :         /* Start after the roots. */
     367           0 :         VerifyNode* node = NextNode(trc->root);
     368           0 :         while ((char*)node < trc->edgeptr) {
     369           0 :             cetrc.node = node;
     370           0 :             js::TraceChildren(&cetrc, node->thing, node->kind);
     371             : 
     372           0 :             if (node->count <= MAX_VERIFIER_EDGES) {
     373           0 :                 for (uint32_t i = 0; i < node->count; i++) {
     374           0 :                     EdgeValue& edge = node->edges[i];
     375           0 :                     if (!IsMarkedOrAllocated(edge)) {
     376             :                         char msgbuf[1024];
     377           0 :                         SprintfLiteral(msgbuf,
     378             :                                        "[barrier verifier] Unmarked edge: %s %p '%s' edge to %s %p",
     379             :                                        JS::GCTraceKindToAscii(node->kind), node->thing,
     380             :                                        edge.label,
     381           0 :                                        JS::GCTraceKindToAscii(edge.kind), edge.thing);
     382           0 :                         MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     383           0 :                         MOZ_CRASH();
     384             :                     }
     385             :                 }
     386             :             }
     387             : 
     388             :             node = NextNode(node);
     389             :         }
     390             :     }
     391             : 
     392           0 :     marker.reset();
     393           0 :     marker.stop();
     394             : 
     395           0 :     js_delete(trc);
     396             : }
     397             : 
     398             : /*** Barrier Verifier Scheduling ***/
     399             : 
     400             : void
     401           0 : gc::GCRuntime::verifyPreBarriers()
     402             : {
     403           0 :     if (verifyPreData)
     404           0 :         endVerifyPreBarriers();
     405             :     else
     406           0 :         startVerifyPreBarriers();
     407           0 : }
     408             : 
     409             : void
     410           0 : gc::VerifyBarriers(JSRuntime* rt, VerifierType type)
     411             : {
     412           0 :     if (GCRuntime::temporaryAbortIfWasmGc(rt->mainContextFromOwnThread()))
     413             :         return;
     414           0 :     if (type == PreBarrierVerifier)
     415           0 :         rt->gc.verifyPreBarriers();
     416             : }
     417             : 
     418             : void
     419      606282 : gc::GCRuntime::maybeVerifyPreBarriers(bool always)
     420             : {
     421           0 :     if (!hasZealMode(ZealMode::VerifierPre))
     422             :         return;
     423             : 
     424           0 :     if (rt->mainContextFromOwnThread()->suppressGC)
     425             :         return;
     426             : 
     427           0 :     if (verifyPreData) {
     428           0 :         if (++verifyPreData->count < zealFrequency && !always)
     429             :             return;
     430             : 
     431           0 :         endVerifyPreBarriers();
     432             :     }
     433             : 
     434           0 :     startVerifyPreBarriers();
     435             : }
     436             : 
     437             : void
     438      606280 : js::gc::MaybeVerifyBarriers(JSContext* cx, bool always)
     439             : {
     440           0 :     GCRuntime* gc = &cx->runtime()->gc;
     441      606280 :     gc->maybeVerifyPreBarriers(always);
     442           0 : }
     443             : 
     444             : void
     445           0 : js::gc::GCRuntime::finishVerifier()
     446             : {
     447           0 :     if (verifyPreData) {
     448           0 :         js_delete(verifyPreData.ref());
     449           0 :         verifyPreData = nullptr;
     450             :     }
     451           0 : }
     452             : 
     453             : #endif /* JS_GC_ZEAL */
     454             : 
     455             : #if defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
     456             : 
     457           0 : class HeapCheckTracerBase : public JS::CallbackTracer
     458             : {
     459             :   public:
     460             :     explicit HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind);
     461             :     bool init();
     462             :     bool traceHeap(AutoTraceSession& session);
     463             :     virtual void checkCell(Cell* cell) = 0;
     464             : 
     465             :   protected:
     466             :     void dumpCellInfo(Cell* cell);
     467             :     void dumpCellPath();
     468             : 
     469             :     Cell* parentCell() {
     470           0 :         return parentIndex == -1 ? nullptr : stack[parentIndex].thing.asCell();
     471             :     }
     472             : 
     473             :     size_t failures;
     474             : 
     475             :   private:
     476             :     void onChild(const JS::GCCellPtr& thing) override;
     477             : 
     478             :     struct WorkItem {
     479             :         WorkItem(JS::GCCellPtr thing, const char* name, int parentIndex)
     480           0 :           : thing(thing), name(name), parentIndex(parentIndex), processed(false)
     481             :         {}
     482             : 
     483             :         JS::GCCellPtr thing;
     484             :         const char* name;
     485             :         int parentIndex;
     486             :         bool processed;
     487             :     };
     488             : 
     489             :     JSRuntime* rt;
     490             :     bool oom;
     491             :     HashSet<Cell*, DefaultHasher<Cell*>, SystemAllocPolicy> visited;
     492             :     Vector<WorkItem, 0, SystemAllocPolicy> stack;
     493             :     int parentIndex;
     494             : };
     495             : 
     496           0 : HeapCheckTracerBase::HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind)
     497             :   : CallbackTracer(rt, weakTraceKind),
     498             :     failures(0),
     499             :     rt(rt),
     500             :     oom(false),
     501           0 :     parentIndex(-1)
     502             : {
     503             : #ifdef DEBUG
     504           0 :     setCheckEdges(false);
     505             : #endif
     506           0 : }
     507             : 
     508             : bool
     509           0 : HeapCheckTracerBase::init()
     510             : {
     511           0 :     return visited.init();
     512             : }
     513             : 
     514             : void
     515           0 : HeapCheckTracerBase::onChild(const JS::GCCellPtr& thing)
     516             : {
     517           0 :     Cell* cell = thing.asCell();
     518           0 :     checkCell(cell);
     519             : 
     520           0 :     if (visited.lookup(cell))
     521           0 :         return;
     522             : 
     523           0 :     if (!visited.put(cell)) {
     524           0 :         oom = true;
     525           0 :         return;
     526             :     }
     527             : 
     528             :     // Don't trace into GC things owned by another runtime.
     529           0 :     if (cell->runtimeFromAnyThread() != rt)
     530             :         return;
     531             : 
     532             :     // Don't trace into GC in zones being used by helper threads.
     533             :     Zone* zone;
     534           0 :     if (thing.is<JSObject>())
     535           0 :         zone = thing.as<JSObject>().zone();
     536           0 :     else if (thing.is<JSString>())
     537           0 :         zone = thing.as<JSString>().zone();
     538             :     else
     539           0 :         zone = cell->asTenured().zone();
     540             : 
     541           0 :     if (zone->usedByHelperThread())
     542             :         return;
     543             : 
     544           0 :     WorkItem item(thing, contextName(), parentIndex);
     545           0 :     if (!stack.append(item))
     546           0 :         oom = true;
     547             : }
     548             : 
     549             : bool
     550           0 : HeapCheckTracerBase::traceHeap(AutoTraceSession& session)
     551             : {
     552             :     // The analysis thinks that traceRuntime might GC by calling a GC callback.
     553           0 :     JS::AutoSuppressGCAnalysis nogc;
     554           0 :     if (!rt->isBeingDestroyed())
     555           0 :         rt->gc.traceRuntime(this, session);
     556             : 
     557           0 :     while (!stack.empty() && !oom) {
     558           0 :         WorkItem item = stack.back();
     559           0 :         if (item.processed) {
     560           0 :             stack.popBack();
     561             :         } else {
     562           0 :             parentIndex = stack.length() - 1;
     563           0 :             stack.back().processed = true;
     564           0 :             TraceChildren(this, item.thing);
     565             :         }
     566             :     }
     567             : 
     568           0 :     return !oom;
     569             : }
     570             : 
     571             : static const char*
     572           0 : GetCellColorName(Cell* cell)
     573             : {
     574           0 :     if (cell->isMarkedBlack())
     575             :         return "black";
     576           0 :     if (cell->isMarkedGray())
     577             :         return "gray";
     578           0 :     return "white";
     579             : }
     580             : 
     581             : void
     582           0 : HeapCheckTracerBase::dumpCellInfo(Cell* cell)
     583             : {
     584           0 :     auto kind = cell->getTraceKind();
     585           0 :     JSObject* obj = kind == JS::TraceKind::Object ? static_cast<JSObject*>(cell) : nullptr;
     586             : 
     587           0 :     fprintf(stderr, "%s %s", GetCellColorName(cell), GCTraceKindToAscii(kind));
     588           0 :     if (obj)
     589           0 :         fprintf(stderr, " %s", obj->getClass()->name);
     590           0 :     fprintf(stderr, " %p", cell);
     591           0 :     if (obj)
     592           0 :         fprintf(stderr, " (compartment %p)", obj->compartment());
     593           0 : }
     594             : 
     595             : void
     596           0 : HeapCheckTracerBase::dumpCellPath()
     597             : {
     598           0 :     const char* name = contextName();
     599           0 :     for (int index = parentIndex; index != -1; index = stack[index].parentIndex) {
     600           0 :         const WorkItem& parent = stack[index];
     601           0 :         Cell* cell = parent.thing.asCell();
     602           0 :         fprintf(stderr, "  from ");
     603           0 :         dumpCellInfo(cell);
     604           0 :         fprintf(stderr, " %s edge\n", name);
     605           0 :         name = parent.name;
     606             :     }
     607           0 :     fprintf(stderr, "  from root %s\n", name);
     608           0 : }
     609             : 
     610             : #endif // defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
     611             : 
     612             : #ifdef JSGC_HASH_TABLE_CHECKS
     613             : 
     614           0 : class CheckHeapTracer final : public HeapCheckTracerBase
     615             : {
     616             :   public:
     617             :     explicit CheckHeapTracer(JSRuntime* rt);
     618             :     void check(AutoTraceSession& session);
     619             : 
     620             :   private:
     621             :     void checkCell(Cell* cell) override;
     622             : };
     623             : 
     624           0 : CheckHeapTracer::CheckHeapTracer(JSRuntime* rt)
     625           0 :   : HeapCheckTracerBase(rt, TraceWeakMapKeysValues)
     626           0 : {}
     627             : 
     628             : inline static bool
     629             : IsValidGCThingPointer(Cell* cell)
     630             : {
     631           0 :     return (uintptr_t(cell) & CellAlignMask) == 0;
     632             : }
     633             : 
     634             : void
     635           0 : CheckHeapTracer::checkCell(Cell* cell)
     636             : {
     637           0 :     if (!IsValidGCThingPointer(cell) || !IsGCThingValidAfterMovingGC(cell)) {
     638           0 :         failures++;
     639           0 :         fprintf(stderr, "Bad pointer %p\n", cell);
     640           0 :         dumpCellPath();
     641             :     }
     642           0 : }
     643             : 
     644             : void
     645           0 : CheckHeapTracer::check(AutoTraceSession& session)
     646             : {
     647           0 :     if (!traceHeap(session))
     648             :         return;
     649             : 
     650           0 :     if (failures)
     651           0 :         fprintf(stderr, "Heap check: %zu failure(s)\n", failures);
     652           0 :     MOZ_RELEASE_ASSERT(failures == 0);
     653             : }
     654             : 
     655             : void
     656           0 : js::gc::CheckHeapAfterGC(JSRuntime* rt)
     657             : {
     658           0 :     AutoTraceSession session(rt, JS::HeapState::Tracing);
     659           0 :     CheckHeapTracer tracer(rt);
     660           0 :     if (tracer.init())
     661           0 :         tracer.check(session);
     662           0 : }
     663             : 
     664             : #endif /* JSGC_HASH_TABLE_CHECKS */
     665             : 
     666             : #if defined(JS_GC_ZEAL) || defined(DEBUG)
     667             : 
     668           0 : class CheckGrayMarkingTracer final : public HeapCheckTracerBase
     669             : {
     670             :   public:
     671             :     explicit CheckGrayMarkingTracer(JSRuntime* rt);
     672             :     bool check(AutoTraceSession& session);
     673             : 
     674             :   private:
     675             :     void checkCell(Cell* cell) override;
     676             : };
     677             : 
     678           0 : CheckGrayMarkingTracer::CheckGrayMarkingTracer(JSRuntime* rt)
     679           0 :   : HeapCheckTracerBase(rt, DoNotTraceWeakMaps)
     680             : {
     681             :     // Weak gray->black edges are allowed.
     682           0 :     setTraceWeakEdges(false);
     683           0 : }
     684             : 
     685             : void
     686           0 : CheckGrayMarkingTracer::checkCell(Cell* cell)
     687             : {
     688           0 :     Cell* parent = parentCell();
     689           0 :     if (!parent)
     690             :         return;
     691             : 
     692           0 :     if (parent->isMarkedBlack() && cell->isMarkedGray()) {
     693           0 :         failures++;
     694             : 
     695           0 :         fprintf(stderr, "Found black to gray edge to ");
     696           0 :         dumpCellInfo(cell);
     697           0 :         fprintf(stderr, "\n");
     698           0 :         dumpCellPath();
     699             : 
     700             : #ifdef DEBUG
     701           0 :         if (cell->is<JSObject>()) {
     702           0 :             fprintf(stderr, "\n");
     703           0 :             DumpObject(cell->as<JSObject>(), stderr);
     704             :         }
     705             : #endif
     706             :     }
     707             : }
     708             : 
     709             : bool
     710           0 : CheckGrayMarkingTracer::check(AutoTraceSession& session)
     711             : {
     712           0 :     if (!traceHeap(session))
     713             :         return true; // Ignore failure.
     714             : 
     715           0 :     return failures == 0;
     716             : }
     717             : 
     718             : JS_FRIEND_API(bool)
     719           0 : js::CheckGrayMarkingState(JSRuntime* rt)
     720             : {
     721           0 :     MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
     722           0 :     MOZ_ASSERT(!rt->gc.isIncrementalGCInProgress());
     723           0 :     if (!rt->gc.areGrayBitsValid())
     724             :         return true;
     725             : 
     726           0 :     gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
     727           0 :     AutoTraceSession session(rt, JS::HeapState::Tracing);
     728           0 :     CheckGrayMarkingTracer tracer(rt);
     729           0 :     if (!tracer.init())
     730             :         return true; // Ignore failure
     731             : 
     732             :     return tracer.check(session);
     733             : }
     734             : 
     735             : #endif // defined(JS_GC_ZEAL) || defined(DEBUG)

Generated by: LCOV version 1.13-14-ga5dd952