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

Generated by: LCOV version 1.13