js/src/gc/Verifier.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 #ifdef MOZ_VALGRIND
     8 # include <valgrind/memcheck.h>
     9 #endif
    11 #include "jscntxt.h"
    12 #include "jsgc.h"
    13 #include "jsprf.h"
    15 #include "gc/GCInternals.h"
    16 #include "gc/Zone.h"
    17 #include "js/GCAPI.h"
    18 #include "js/HashTable.h"
    20 #include "jscntxtinlines.h"
    21 #include "jsgcinlines.h"
    23 using namespace js;
    24 using namespace js::gc;
    25 using namespace mozilla;
    27 #ifdef JS_GC_ZEAL
    29 /*
    30  * Write barrier verification
    31  *
    32  * The next few functions are for write barrier verification.
    33  *
    34  * The VerifyBarriers function is a shorthand. It checks if a verification phase
    35  * is currently running. If not, it starts one. Otherwise, it ends the current
    36  * phase and starts a new one.
    37  *
    38  * The user can adjust the frequency of verifications, which causes
    39  * VerifyBarriers to be a no-op all but one out of N calls. However, if the
    40  * |always| parameter is true, it starts a new phase no matter what.
    41  *
    42  * Pre-Barrier Verifier:
    43  *   When StartVerifyBarriers is called, a snapshot is taken of all objects in
    44  *   the GC heap and saved in an explicit graph data structure. Later,
    45  *   EndVerifyBarriers traverses the heap again. Any pointer values that were in
    46  *   the snapshot and are no longer found must be marked; otherwise an assertion
    47  *   triggers. Note that we must not GC in between starting and finishing a
    48  *   verification phase.
    49  *
    50  * Post-Barrier Verifier:
    51  *   When StartVerifyBarriers is called, we create a virtual "Nursery Set" which
    52  *   future allocations are recorded in and turn on the StoreBuffer. Later,
    53  *   EndVerifyBarriers traverses the heap and ensures that the set of cross-
    54  *   generational pointers we find is a subset of the pointers recorded in our
    55  *   StoreBuffer.
    56  */
    58 struct EdgeValue
    59 {
    60     void *thing;
    61     JSGCTraceKind kind;
    62     const char *label;
    63 };
    65 struct VerifyNode
    66 {
    67     void *thing;
    68     JSGCTraceKind kind;
    69     uint32_t count;
    70     EdgeValue edges[1];
    71 };
    73 typedef HashMap<void *, VerifyNode *, DefaultHasher<void *>, SystemAllocPolicy> NodeMap;
    75 /*
    76  * The verifier data structures are simple. The entire graph is stored in a
    77  * single block of memory. At the beginning is a VerifyNode for the root
    78  * node. It is followed by a sequence of EdgeValues--the exact number is given
    79  * in the node. After the edges come more nodes and their edges.
    80  *
    81  * The edgeptr and term fields are used to allocate out of the block of memory
    82  * for the graph. If we run out of memory (i.e., if edgeptr goes beyond term),
    83  * we just abandon the verification.
    84  *
    85  * The nodemap field is a hashtable that maps from the address of the GC thing
    86  * to the VerifyNode that represents it.
    87  */
    88 struct VerifyPreTracer : JSTracer
    89 {
    90     JS::AutoDisableGenerationalGC noggc;
    92     /* The gcNumber when the verification began. */
    93     uint64_t number;
    95     /* This counts up to gcZealFrequency to decide whether to verify. */
    96     int count;
    98     /* This graph represents the initial GC "snapshot". */
    99     VerifyNode *curnode;
   100     VerifyNode *root;
   101     char *edgeptr;
   102     char *term;
   103     NodeMap nodemap;
   105     VerifyPreTracer(JSRuntime *rt, JSTraceCallback callback)
   106       : JSTracer(rt, callback), noggc(rt), number(rt->gcNumber), count(0), root(nullptr)
   107     {}
   109     ~VerifyPreTracer() {
   110         js_free(root);
   111     }
   112 };
   114 /*
   115  * This function builds up the heap snapshot by adding edges to the current
   116  * node.
   117  */
   118 static void
   119 AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
   120 {
   121     VerifyPreTracer *trc = (VerifyPreTracer *)jstrc;
   123     JS_ASSERT(!IsInsideNursery(trc->runtime(), *(uintptr_t **)thingp));
   125     trc->edgeptr += sizeof(EdgeValue);
   126     if (trc->edgeptr >= trc->term) {
   127         trc->edgeptr = trc->term;
   128         return;
   129     }
   131     VerifyNode *node = trc->curnode;
   132     uint32_t i = node->count;
   134     node->edges[i].thing = *thingp;
   135     node->edges[i].kind = kind;
   136     node->edges[i].label = trc->tracingName("<unknown>");
   137     node->count++;
   138 }
   140 static VerifyNode *
   141 MakeNode(VerifyPreTracer *trc, void *thing, JSGCTraceKind kind)
   142 {
   143     NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing);
   144     if (!p) {
   145         VerifyNode *node = (VerifyNode *)trc->edgeptr;
   146         trc->edgeptr += sizeof(VerifyNode) - sizeof(EdgeValue);
   147         if (trc->edgeptr >= trc->term) {
   148             trc->edgeptr = trc->term;
   149             return nullptr;
   150         }
   152         node->thing = thing;
   153         node->count = 0;
   154         node->kind = kind;
   155         trc->nodemap.add(p, thing, node);
   156         return node;
   157     }
   158     return nullptr;
   159 }
   161 static VerifyNode *
   162 NextNode(VerifyNode *node)
   163 {
   164     if (node->count == 0)
   165         return (VerifyNode *)((char *)node + sizeof(VerifyNode) - sizeof(EdgeValue));
   166     else
   167         return (VerifyNode *)((char *)node + sizeof(VerifyNode) +
   168                              sizeof(EdgeValue)*(node->count - 1));
   169 }
   171 void
   172 gc::StartVerifyPreBarriers(JSRuntime *rt)
   173 {
   174     if (rt->gcVerifyPreData || rt->gcIncrementalState != NO_INCREMENTAL)
   175         return;
   177     /*
   178      * The post barrier verifier requires the storebuffer to be enabled, but the
   179      * pre barrier verifier disables it as part of disabling GGC.  Don't allow
   180      * starting the pre barrier verifier if the post barrier verifier is already
   181      * running.
   182      */
   183     if (rt->gcVerifyPostData)
   184         return;
   186     MinorGC(rt, JS::gcreason::EVICT_NURSERY);
   188     AutoPrepareForTracing prep(rt, WithAtoms);
   190     if (!IsIncrementalGCSafe(rt))
   191         return;
   193     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
   194         r.front()->bitmap.clear();
   196     rt->gcNumber++;
   198     VerifyPreTracer *trc = js_new<VerifyPreTracer>(rt, JSTraceCallback(nullptr));
   199     if (!trc)
   200         return;
   202     /*
   203      * Passing a function pointer directly to js_new trips a compiler bug in
   204      * MSVC. Work around by filling the pointer after allocating with nullptr.
   205      */
   206     trc->setTraceCallback(AccumulateEdge);
   208     const size_t size = 64 * 1024 * 1024;
   209     trc->root = (VerifyNode *)js_malloc(size);
   210     if (!trc->root)
   211         goto oom;
   212     trc->edgeptr = (char *)trc->root;
   213     trc->term = trc->edgeptr + size;
   215     if (!trc->nodemap.init())
   216         goto oom;
   218     /* Create the root node. */
   219     trc->curnode = MakeNode(trc, nullptr, JSGCTraceKind(0));
   221     /* We want MarkRuntime to save the roots to gcSavedRoots. */
   222     rt->gcIncrementalState = MARK_ROOTS;
   224     /* Make all the roots be edges emanating from the root node. */
   225     MarkRuntime(trc);
   227     VerifyNode *node;
   228     node = trc->curnode;
   229     if (trc->edgeptr == trc->term)
   230         goto oom;
   232     /* For each edge, make a node for it if one doesn't already exist. */
   233     while ((char *)node < trc->edgeptr) {
   234         for (uint32_t i = 0; i < node->count; i++) {
   235             EdgeValue &e = node->edges[i];
   236             VerifyNode *child = MakeNode(trc, e.thing, e.kind);
   237             if (child) {
   238                 trc->curnode = child;
   239                 JS_TraceChildren(trc, e.thing, e.kind);
   240             }
   241             if (trc->edgeptr == trc->term)
   242                 goto oom;
   243         }
   245         node = NextNode(node);
   246     }
   248     rt->gcVerifyPreData = trc;
   249     rt->gcIncrementalState = MARK;
   250     rt->gcMarker.start();
   252     rt->setNeedsBarrier(true);
   253     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
   254         PurgeJITCaches(zone);
   255         zone->setNeedsBarrier(true, Zone::UpdateIon);
   256         zone->allocator.arenas.purge();
   257     }
   259     return;
   261 oom:
   262     rt->gcIncrementalState = NO_INCREMENTAL;
   263     js_delete(trc);
   264     rt->gcVerifyPreData = nullptr;
   265 }
   267 static bool
   268 IsMarkedOrAllocated(Cell *cell)
   269 {
   270     return cell->isMarked() || cell->arenaHeader()->allocatedDuringIncremental;
   271 }
   273 static const uint32_t MAX_VERIFIER_EDGES = 1000;
   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 static void
   283 CheckEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
   284 {
   285     VerifyPreTracer *trc = (VerifyPreTracer *)jstrc;
   286     VerifyNode *node = trc->curnode;
   288     /* Avoid n^2 behavior. */
   289     if (node->count > MAX_VERIFIER_EDGES)
   290         return;
   292     for (uint32_t i = 0; i < node->count; i++) {
   293         if (node->edges[i].thing == *thingp) {
   294             JS_ASSERT(node->edges[i].kind == kind);
   295             node->edges[i].thing = nullptr;
   296             return;
   297         }
   298     }
   299 }
   301 static void
   302 AssertMarkedOrAllocated(const EdgeValue &edge)
   303 {
   304     if (!edge.thing || IsMarkedOrAllocated(static_cast<Cell *>(edge.thing)))
   305         return;
   307     // Permanent atoms aren't marked during graph traversal.
   308     if (edge.kind == JSTRACE_STRING && static_cast<JSString *>(edge.thing)->isPermanentAtom())
   309         return;
   311     char msgbuf[1024];
   312     const char *label = edge.label;
   314     JS_snprintf(msgbuf, sizeof(msgbuf), "[barrier verifier] Unmarked edge: %s", label);
   315     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
   316     MOZ_CRASH();
   317 }
   319 void
   320 gc::EndVerifyPreBarriers(JSRuntime *rt)
   321 {
   322     JS_ASSERT(!JS::IsGenerationalGCEnabled(rt));
   324     AutoPrepareForTracing prep(rt, SkipAtoms);
   326     VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData;
   328     if (!trc)
   329         return;
   331     bool compartmentCreated = false;
   333     /* We need to disable barriers before tracing, which may invoke barriers. */
   334     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
   335         if (!zone->needsBarrier())
   336             compartmentCreated = true;
   338         zone->setNeedsBarrier(false, Zone::UpdateIon);
   339         PurgeJITCaches(zone);
   340     }
   341     rt->setNeedsBarrier(false);
   343     /*
   344      * We need to bump gcNumber so that the methodjit knows that jitcode has
   345      * been discarded.
   346      */
   347     JS_ASSERT(trc->number == rt->gcNumber);
   348     rt->gcNumber++;
   350     rt->gcVerifyPreData = nullptr;
   351     rt->gcIncrementalState = NO_INCREMENTAL;
   353     if (!compartmentCreated && IsIncrementalGCSafe(rt)) {
   354         trc->setTraceCallback(CheckEdge);
   356         /* Start after the roots. */
   357         VerifyNode *node = NextNode(trc->root);
   358         while ((char *)node < trc->edgeptr) {
   359             trc->curnode = node;
   360             JS_TraceChildren(trc, node->thing, node->kind);
   362             if (node->count <= MAX_VERIFIER_EDGES) {
   363                 for (uint32_t i = 0; i < node->count; i++)
   364                     AssertMarkedOrAllocated(node->edges[i]);
   365             }
   367             node = NextNode(node);
   368         }
   369     }
   371     rt->gcMarker.reset();
   372     rt->gcMarker.stop();
   374     js_delete(trc);
   375 }
   377 /*** Post-Barrier Verifyier ***/
   379 struct VerifyPostTracer : JSTracer
   380 {
   381     /* The gcNumber when the verification began. */
   382     uint64_t number;
   384     /* This counts up to gcZealFrequency to decide whether to verify. */
   385     int count;
   387     /* The set of edges in the StoreBuffer at the end of verification. */
   388     typedef HashSet<void **, PointerHasher<void **, 3>, SystemAllocPolicy> EdgeSet;
   389     EdgeSet *edges;
   391     VerifyPostTracer(JSRuntime *rt, JSTraceCallback callback)
   392       : JSTracer(rt, callback), number(rt->gcNumber), count(0)
   393     {}
   394 };
   396 /*
   397  * The post-barrier verifier runs the full store buffer and a fake nursery when
   398  * running and when it stops, walks the full heap to ensure that all the
   399  * important edges were inserted into the storebuffer.
   400  */
   401 void
   402 gc::StartVerifyPostBarriers(JSRuntime *rt)
   403 {
   404 #ifdef JSGC_GENERATIONAL
   405     if (rt->gcVerifyPostData ||
   406         rt->gcIncrementalState != NO_INCREMENTAL)
   407     {
   408         return;
   409     }
   411     MinorGC(rt, JS::gcreason::EVICT_NURSERY);
   413     rt->gcNumber++;
   415     VerifyPostTracer *trc = js_new<VerifyPostTracer>(rt, JSTraceCallback(nullptr));
   416     if (!trc)
   417         return;
   419     rt->gcVerifyPostData = trc;
   420 #endif
   421 }
   423 #ifdef JSGC_GENERATIONAL
   424 void
   425 PostVerifierCollectStoreBufferEdges(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
   426 {
   427     VerifyPostTracer *trc = (VerifyPostTracer *)jstrc;
   429     /* The nursery only stores objects. */
   430     if (kind != JSTRACE_OBJECT)
   431         return;
   433     /* The store buffer may store extra, non-cross-generational edges. */
   434     JSObject *dst = *reinterpret_cast<JSObject **>(thingp);
   435     if (trc->runtime()->gcNursery.isInside(thingp) || !trc->runtime()->gcNursery.isInside(dst))
   436         return;
   438     /*
   439      * Values will be unpacked to the stack before getting here. However, the
   440      * only things that enter this callback are marked by the store buffer. The
   441      * store buffer ensures that the real tracing location is set correctly.
   442      */
   443     void **loc = trc->tracingLocation(thingp);
   445     trc->edges->put(loc);
   446 }
   448 static void
   449 AssertStoreBufferContainsEdge(VerifyPostTracer::EdgeSet *edges, void **loc, JSObject *dst)
   450 {
   451     if (edges->has(loc))
   452         return;
   454     char msgbuf[1024];
   455     JS_snprintf(msgbuf, sizeof(msgbuf), "[post-barrier verifier] Missing edge @ %p to %p",
   456                 (void *)loc, (void *)dst);
   457     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
   458     MOZ_CRASH();
   459 }
   461 void
   462 PostVerifierVisitEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
   463 {
   464     VerifyPostTracer *trc = (VerifyPostTracer *)jstrc;
   466     /* The nursery only stores objects. */
   467     if (kind != JSTRACE_OBJECT)
   468         return;
   470     /* Filter out non cross-generational edges. */
   471     JS_ASSERT(!trc->runtime()->gcNursery.isInside(thingp));
   472     JSObject *dst = *reinterpret_cast<JSObject **>(thingp);
   473     if (!trc->runtime()->gcNursery.isInside(dst))
   474         return;
   476     /*
   477      * Values will be unpacked to the stack before getting here. However, the
   478      * only things that enter this callback are marked by the JS_TraceChildren
   479      * below. Since ObjectImpl::markChildren handles this, the real trace
   480      * location will be set correctly in these cases.
   481      */
   482     void **loc = trc->tracingLocation(thingp);
   484     AssertStoreBufferContainsEdge(trc->edges, loc, dst);
   485 }
   486 #endif
   488 void
   489 js::gc::EndVerifyPostBarriers(JSRuntime *rt)
   490 {
   491 #ifdef JSGC_GENERATIONAL
   492     VerifyPostTracer::EdgeSet edges;
   493     AutoPrepareForTracing prep(rt, SkipAtoms);
   495     VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData;
   497     /* Visit every entry in the store buffer and put the edges in a hash set. */
   498     trc->setTraceCallback(PostVerifierCollectStoreBufferEdges);
   499     if (!edges.init())
   500         goto oom;
   501     trc->edges = &edges;
   502     rt->gcStoreBuffer.markAll(trc);
   504     /* Walk the heap to find any edges not the the |edges| set. */
   505     trc->setTraceCallback(PostVerifierVisitEdge);
   506     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
   507         for (size_t kind = 0; kind < FINALIZE_LIMIT; ++kind) {
   508             for (CellIterUnderGC cells(zone, AllocKind(kind)); !cells.done(); cells.next()) {
   509                 Cell *src = cells.getCell();
   510                 JS_TraceChildren(trc, src, MapAllocToTraceKind(AllocKind(kind)));
   511             }
   512         }
   513     }
   515 oom:
   516     js_delete(trc);
   517     rt->gcVerifyPostData = nullptr;
   518 #endif
   519 }
   521 /*** Barrier Verifier Scheduling ***/
   523 static void
   524 VerifyPreBarriers(JSRuntime *rt)
   525 {
   526     if (rt->gcVerifyPreData)
   527         EndVerifyPreBarriers(rt);
   528     else
   529         StartVerifyPreBarriers(rt);
   530 }
   532 static void
   533 VerifyPostBarriers(JSRuntime *rt)
   534 {
   535     if (rt->gcVerifyPostData)
   536         EndVerifyPostBarriers(rt);
   537     else
   538         StartVerifyPostBarriers(rt);
   539 }
   541 void
   542 gc::VerifyBarriers(JSRuntime *rt, VerifierType type)
   543 {
   544     if (type == PreBarrierVerifier)
   545         VerifyPreBarriers(rt);
   546     else
   547         VerifyPostBarriers(rt);
   548 }
   550 static void
   551 MaybeVerifyPreBarriers(JSRuntime *rt, bool always)
   552 {
   553     if (rt->gcZeal() != ZealVerifierPreValue)
   554         return;
   556     if (rt->mainThread.suppressGC)
   557         return;
   559     if (VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData) {
   560         if (++trc->count < rt->gcZealFrequency && !always)
   561             return;
   563         EndVerifyPreBarriers(rt);
   564     }
   566     StartVerifyPreBarriers(rt);
   567 }
   569 static void
   570 MaybeVerifyPostBarriers(JSRuntime *rt, bool always)
   571 {
   572 #ifdef JSGC_GENERATIONAL
   573     if (rt->gcZeal() != ZealVerifierPostValue)
   574         return;
   576     if (rt->mainThread.suppressGC || !rt->gcStoreBuffer.isEnabled())
   577         return;
   579     if (VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData) {
   580         if (++trc->count < rt->gcZealFrequency && !always)
   581             return;
   583         EndVerifyPostBarriers(rt);
   584     }
   585     StartVerifyPostBarriers(rt);
   586 #endif
   587 }
   589 void
   590 js::gc::MaybeVerifyBarriers(JSContext *cx, bool always)
   591 {
   592     MaybeVerifyPreBarriers(cx->runtime(), always);
   593     MaybeVerifyPostBarriers(cx->runtime(), always);
   594 }
   596 void
   597 js::gc::FinishVerifier(JSRuntime *rt)
   598 {
   599     if (VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData) {
   600         js_delete(trc);
   601         rt->gcVerifyPreData = nullptr;
   602     }
   603 #ifdef JSGC_GENERATIONAL
   604     if (VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData) {
   605         js_delete(trc);
   606         rt->gcVerifyPostData = nullptr;
   607     }
   608 #endif
   609 }
   611 #endif /* JS_GC_ZEAL */

mercurial