LCOV - code coverage report
Current view: top level - js/src/gc - Zone.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 30 211 14.2 %
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 "gc/Zone.h"
       8             : 
       9             : #include "gc/FreeOp.h"
      10             : #include "gc/Policy.h"
      11             : #include "gc/PublicIterators.h"
      12             : #include "jit/BaselineJIT.h"
      13             : #include "jit/Ion.h"
      14             : #include "jit/JitRealm.h"
      15             : #include "vm/Debugger.h"
      16             : #include "vm/Runtime.h"
      17             : 
      18             : #include "gc/GC-inl.h"
      19             : #include "gc/Marking-inl.h"
      20             : #include "vm/Realm-inl.h"
      21             : 
      22             : using namespace js;
      23             : using namespace js::gc;
      24             : 
      25             : Zone * const Zone::NotOnList = reinterpret_cast<Zone*>(1);
      26             : 
      27           0 : JS::Zone::Zone(JSRuntime* rt)
      28             :   : JS::shadow::Zone(rt, &rt->gc.marker),
      29             :     // Note: don't use |this| before initializing helperThreadUse_!
      30             :     // ProtectedData checks in CheckZone::check may read this field.
      31             :     helperThreadUse_(HelperThreadUse::None),
      32             :     helperThreadOwnerContext_(nullptr),
      33             :     debuggers(this, nullptr),
      34             :     uniqueIds_(this),
      35             :     suppressAllocationMetadataBuilder(this, false),
      36             :     arenas(rt, this),
      37             :     types(this),
      38             :     gcWeakMapList_(this),
      39             :     compartments_(),
      40             :     gcGrayRoots_(this),
      41             :     gcWeakRefs_(this),
      42             :     weakCaches_(this),
      43           0 :     gcWeakKeys_(this, SystemAllocPolicy(), rt->randomHashCodeScrambler()),
      44             :     typeDescrObjects_(this, this),
      45             :     regExps(this),
      46             :     markedAtoms_(this),
      47             :     atomCache_(this),
      48             :     externalStringCache_(this),
      49             :     functionToStringCache_(this),
      50             :     keepAtomsCount(this, 0),
      51             :     purgeAtomsDeferred(this, 0),
      52             :     usage(&rt->gc.usage),
      53             :     threshold(),
      54             :     gcDelayBytes(0),
      55             :     tenuredStrings(this, 0),
      56             :     allocNurseryStrings(this, true),
      57             :     propertyTree_(this, this),
      58             :     baseShapes_(this, this),
      59             :     initialShapes_(this, this),
      60             :     nurseryShapes_(this),
      61             :     data(this, nullptr),
      62             :     isSystem(this, false),
      63             : #ifdef DEBUG
      64             :     gcLastSweepGroupIndex(this, 0),
      65             : #endif
      66             :     jitZone_(this, nullptr),
      67             :     gcScheduled_(false),
      68             :     gcScheduledSaved_(false),
      69             :     gcPreserveCode_(this, false),
      70             :     keepShapeTables_(this, false),
      71         882 :     listNext_(NotOnList)
      72             : {
      73             :     /* Ensure that there are no vtables to mess us up here. */
      74             :     MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
      75             :                static_cast<JS::shadow::Zone*>(this));
      76             : 
      77           0 :     AutoLockGC lock(rt);
      78           0 :     threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState, lock);
      79           0 :     setGCMaxMallocBytes(rt->gc.tunables.maxMallocBytes(), lock);
      80          21 :     jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8, lock);
      81           0 : }
      82             : 
      83           0 : Zone::~Zone()
      84             : {
      85           0 :     MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::None);
      86             : 
      87           0 :     JSRuntime* rt = runtimeFromAnyThread();
      88          10 :     if (this == rt->gc.systemZone)
      89           0 :         rt->gc.systemZone = nullptr;
      90             : 
      91          10 :     js_delete(debuggers.ref());
      92          10 :     js_delete(jitZone_.ref());
      93             : 
      94             : #ifdef DEBUG
      95             :     // Avoid assertions failures warning that not everything has been destroyed
      96             :     // if the embedding leaked GC things.
      97           0 :     if (!rt->gc.shutdownCollectedEverything()) {
      98           0 :         gcWeakMapList().clear();
      99           0 :         regExps.clear();
     100             :     }
     101             : #endif
     102           5 : }
     103             : 
     104             : bool
     105           0 : Zone::init(bool isSystemArg)
     106             : {
     107           0 :     isSystem = isSystemArg;
     108           0 :     return uniqueIds().init() &&
     109           0 :            gcSweepGroupEdges().init() &&
     110           0 :            gcWeakKeys().init() &&
     111           0 :            typeDescrObjects().init() &&
     112           0 :            markedAtoms().init() &&
     113          84 :            atomCache().init() &&
     114          42 :            regExps.init();
     115             : }
     116             : 
     117             : void
     118           0 : Zone::setNeedsIncrementalBarrier(bool needs)
     119             : {
     120           0 :     MOZ_ASSERT_IF(needs, canCollect());
     121           0 :     needsIncrementalBarrier_ = needs;
     122           0 : }
     123             : 
     124             : void
     125           0 : Zone::beginSweepTypes(bool releaseTypes)
     126             : {
     127           0 :     types.beginSweep(releaseTypes);
     128           0 : }
     129             : 
     130             : Zone::DebuggerVector*
     131           0 : Zone::getOrCreateDebuggers(JSContext* cx)
     132             : {
     133           0 :     if (debuggers)
     134           0 :         return debuggers;
     135             : 
     136           0 :     debuggers = js_new<DebuggerVector>();
     137           0 :     if (!debuggers)
     138           0 :         ReportOutOfMemory(cx);
     139           0 :     return debuggers;
     140             : }
     141             : 
     142             : void
     143           0 : Zone::sweepBreakpoints(FreeOp* fop)
     144             : {
     145           0 :     if (fop->runtime()->debuggerList().isEmpty())
     146             :         return;
     147             : 
     148             :     /*
     149             :      * Sweep all compartments in a zone at the same time, since there is no way
     150             :      * to iterate over the scripts belonging to a single compartment in a zone.
     151             :      */
     152             : 
     153           0 :     MOZ_ASSERT(isGCSweepingOrCompacting());
     154           0 :     for (auto iter = cellIter<JSScript>(); !iter.done(); iter.next()) {
     155           0 :         JSScript* script = iter;
     156           0 :         if (!script->hasAnyBreakpointsOrStepMode())
     157           0 :             continue;
     158             : 
     159           0 :         bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
     160           0 :         MOZ_ASSERT(script == iter);
     161           0 :         for (unsigned i = 0; i < script->length(); i++) {
     162           0 :             BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
     163           0 :             if (!site)
     164             :                 continue;
     165             : 
     166             :             Breakpoint* nextbp;
     167           0 :             for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
     168           0 :                 nextbp = bp->nextInSite();
     169           0 :                 GCPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
     170             : 
     171             :                 // If we are sweeping, then we expect the script and the
     172             :                 // debugger object to be swept in the same sweep group, except
     173             :                 // if the breakpoint was added after we computed the sweep
     174             :                 // groups. In this case both script and debugger object must be
     175             :                 // live.
     176           0 :                 MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(),
     177             :                               dbgobj->zone()->isGCSweeping() ||
     178             :                               (!scriptGone && dbgobj->asTenured().isMarkedAny()));
     179             : 
     180           0 :                 bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj);
     181           0 :                 MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
     182           0 :                 if (dying)
     183           0 :                     bp->destroy(fop);
     184             :             }
     185             :         }
     186             :     }
     187             : }
     188             : 
     189             : void
     190           0 : Zone::sweepWeakMaps()
     191             : {
     192             :     /* Finalize unreachable (key,value) pairs in all weak maps. */
     193           0 :     WeakMapBase::sweepZone(this);
     194           0 : }
     195             : 
     196             : void
     197           0 : Zone::discardJitCode(FreeOp* fop, bool discardBaselineCode)
     198             : {
     199           0 :     if (!jitZone())
     200             :         return;
     201             : 
     202           0 :     if (isPreservingCode())
     203             :         return;
     204             : 
     205           0 :     if (discardBaselineCode) {
     206             : #ifdef DEBUG
     207             :         /* Assert no baseline scripts are marked as active. */
     208           0 :         for (auto script = cellIter<JSScript>(); !script.done(); script.next())
     209           0 :             MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
     210             : #endif
     211             : 
     212             :         /* Mark baseline scripts on the stack as active. */
     213           0 :         jit::MarkActiveBaselineScripts(this);
     214             :     }
     215             : 
     216             :     /* Only mark OSI points if code is being discarded. */
     217           0 :     jit::InvalidateAll(fop, this);
     218             : 
     219           0 :     for (auto script = cellIter<JSScript>(); !script.done(); script.next())  {
     220           0 :         jit::FinishInvalidation(fop, script);
     221             : 
     222             :         /*
     223             :          * Discard baseline script if it's not marked as active. Note that
     224             :          * this also resets the active flag.
     225             :          */
     226           0 :         if (discardBaselineCode)
     227           0 :             jit::FinishDiscardBaselineScript(fop, script);
     228             : 
     229             :         /*
     230             :          * Warm-up counter for scripts are reset on GC. After discarding code we
     231             :          * need to let it warm back up to get information such as which
     232             :          * opcodes are setting array holes or accessing getter properties.
     233             :          */
     234           0 :         script->resetWarmUpCounter();
     235             : 
     236             :         /*
     237             :          * Make it impossible to use the control flow graphs cached on the
     238             :          * BaselineScript. They get deleted.
     239             :          */
     240           0 :         if (script->hasBaselineScript())
     241           0 :             script->baselineScript()->setControlFlowGraph(nullptr);
     242             :     }
     243             : 
     244             :     /*
     245             :      * When scripts contains pointers to nursery things, the store buffer
     246             :      * can contain entries that point into the optimized stub space. Since
     247             :      * this method can be called outside the context of a GC, this situation
     248             :      * could result in us trying to mark invalid store buffer entries.
     249             :      *
     250             :      * Defer freeing any allocated blocks until after the next minor GC.
     251             :      */
     252           0 :     if (discardBaselineCode) {
     253           0 :         jitZone()->optimizedStubSpace()->freeAllAfterMinorGC(this);
     254           0 :         jitZone()->purgeIonCacheIRStubInfo();
     255             :     }
     256             : 
     257             :     /*
     258             :      * Free all control flow graphs that are cached on BaselineScripts.
     259             :      * Assuming this happens on the main thread and all control flow
     260             :      * graph reads happen on the main thread, this is safe.
     261             :      */
     262           0 :     jitZone()->cfgSpace()->lifoAlloc().freeAll();
     263             : }
     264             : 
     265             : #ifdef JSGC_HASH_TABLE_CHECKS
     266             : void
     267           0 : JS::Zone::checkUniqueIdTableAfterMovingGC()
     268             : {
     269           0 :     for (auto r = uniqueIds().all(); !r.empty(); r.popFront())
     270           0 :         js::gc::CheckGCThingAfterMovingGC(r.front().key());
     271           0 : }
     272             : #endif
     273             : 
     274             : uint64_t
     275      137281 : Zone::gcNumber()
     276             : {
     277             :     // Zones in use by exclusive threads are not collected, and threads using
     278             :     // them cannot access the main runtime's gcNumber without racing.
     279      273602 :     return usedByHelperThread() ? 0 : runtimeFromMainThread()->gc.gcNumber();
     280             : }
     281             : 
     282             : js::jit::JitZone*
     283           0 : Zone::createJitZone(JSContext* cx)
     284             : {
     285           0 :     MOZ_ASSERT(!jitZone_);
     286             : 
     287          10 :     if (!cx->runtime()->getJitRuntime(cx))
     288             :         return nullptr;
     289             : 
     290          30 :     UniquePtr<jit::JitZone> jitZone(cx->new_<js::jit::JitZone>());
     291          10 :     if (!jitZone || !jitZone->init(cx))
     292             :         return nullptr;
     293             : 
     294          20 :     jitZone_ = jitZone.release();
     295          20 :     return jitZone_;
     296             : }
     297             : 
     298             : bool
     299           0 : Zone::hasMarkedRealms()
     300             : {
     301           0 :     for (RealmsInZoneIter realm(this); !realm.done(); realm.next()) {
     302           0 :         if (realm->marked())
     303           0 :             return true;
     304             :     }
     305           0 :     return false;
     306             : }
     307             : 
     308             : bool
     309           0 : Zone::canCollect()
     310             : {
     311             :     // The atoms zone cannot be collected while off-thread parsing is taking
     312             :     // place.
     313           0 :     if (isAtomsZone())
     314           0 :         return !runtimeFromAnyThread()->hasHelperThreadZones();
     315             : 
     316             :     // Zones that will be or are currently used by other threads cannot be
     317             :     // collected.
     318           0 :     return !createdForHelperThread();
     319             : }
     320             : 
     321             : void
     322           0 : Zone::notifyObservingDebuggers()
     323             : {
     324           0 :     JSRuntime* rt = runtimeFromMainThread();
     325           0 :     JSContext* cx = rt->mainContextFromOwnThread();
     326             : 
     327           0 :     for (RealmsInZoneIter realms(this); !realms.done(); realms.next()) {
     328           0 :         RootedGlobalObject global(cx, realms->unsafeUnbarrieredMaybeGlobal());
     329           0 :         if (!global)
     330           0 :             continue;
     331             : 
     332           0 :         GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
     333           0 :         if (!dbgs)
     334             :             continue;
     335             : 
     336           0 :         for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty(); r.popFront()) {
     337           0 :             if (!r.front()->debuggeeIsBeingCollected(rt->gc.majorGCCount())) {
     338             : #ifdef DEBUG
     339             :                 fprintf(stderr,
     340             :                         "OOM while notifying observing Debuggers of a GC: The onGarbageCollection\n"
     341           0 :                         "hook will not be fired for this GC for some Debuggers!\n");
     342             : #endif
     343           0 :                 return;
     344             :             }
     345             :         }
     346             :     }
     347             : }
     348             : 
     349             : bool
     350           0 : Zone::isOnList() const
     351             : {
     352           0 :     return listNext_ != NotOnList;
     353             : }
     354             : 
     355             : Zone*
     356           0 : Zone::nextZone() const
     357             : {
     358           0 :     MOZ_ASSERT(isOnList());
     359           0 :     return listNext_;
     360             : }
     361             : 
     362             : void
     363           0 : Zone::clearTables()
     364             : {
     365           0 :     MOZ_ASSERT(regExps.empty());
     366             : 
     367           0 :     if (baseShapes().initialized())
     368           0 :         baseShapes().clear();
     369           0 :     if (initialShapes().initialized())
     370           5 :         initialShapes().clear();
     371           5 : }
     372             : 
     373             : void
     374           0 : Zone::fixupAfterMovingGC()
     375             : {
     376           0 :     fixupInitialShapeTable();
     377           0 : }
     378             : 
     379             : bool
     380          84 : Zone::addTypeDescrObject(JSContext* cx, HandleObject obj)
     381             : {
     382             :     // Type descriptor objects are always tenured so we don't need post barriers
     383             :     // on the set.
     384           0 :     MOZ_ASSERT(!IsInsideNursery(obj));
     385             : 
     386           0 :     if (!typeDescrObjects().put(obj)) {
     387           0 :         ReportOutOfMemory(cx);
     388           0 :         return false;
     389             :     }
     390             : 
     391             :     return true;
     392             : }
     393             : 
     394             : void
     395           0 : Zone::deleteEmptyCompartment(JS::Compartment* comp)
     396             : {
     397           5 :     MOZ_ASSERT(comp->zone() == this);
     398           0 :     MOZ_ASSERT(arenas.checkEmptyArenaLists());
     399             : 
     400           0 :     MOZ_ASSERT(compartments().length() == 1);
     401           5 :     MOZ_ASSERT(compartments()[0] == comp);
     402           0 :     MOZ_ASSERT(comp->realms().length() == 1);
     403             : 
     404           5 :     Realm* realm = comp->realms()[0];
     405           0 :     FreeOp* fop = runtimeFromMainThread()->defaultFreeOp();
     406           0 :     realm->destroy(fop);
     407           5 :     comp->destroy(fop);
     408             : 
     409           0 :     compartments().clear();
     410           5 : }
     411             : 
     412             : void
     413           0 : Zone::setHelperThreadOwnerContext(JSContext* cx)
     414             : {
     415          15 :     MOZ_ASSERT_IF(cx, TlsContext.get() == cx);
     416           0 :     helperThreadOwnerContext_ = cx;
     417          10 : }
     418             : 
     419             : bool
     420           0 : Zone::ownedByCurrentHelperThread()
     421             : {
     422       26861 :     MOZ_ASSERT(usedByHelperThread());
     423           0 :     MOZ_ASSERT(TlsContext.get());
     424           0 :     return helperThreadOwnerContext_ == TlsContext.get();
     425             : }
     426             : 
     427           0 : void Zone::releaseAtoms()
     428             : {
     429        2450 :     MOZ_ASSERT(hasKeptAtoms());
     430             : 
     431           0 :     keepAtomsCount--;
     432             : 
     433        3675 :     if (!hasKeptAtoms() && purgeAtomsDeferred) {
     434           0 :         atomCache().clearAndShrink();
     435           0 :         purgeAtomsDeferred = false;
     436             :     }
     437           0 : }
     438             : 
     439             : void
     440           0 : Zone::purgeAtomCacheOrDefer()
     441             : {
     442           0 :     if (hasKeptAtoms()) {
     443           0 :         purgeAtomsDeferred = true;
     444           0 :         return;
     445             :     }
     446             : 
     447           0 :     atomCache().clearAndShrink();
     448             : }
     449             : 
     450             : void
     451           0 : Zone::traceAtomCache(JSTracer* trc)
     452             : {
     453           0 :     MOZ_ASSERT(hasKeptAtoms());
     454           0 :     for (auto r = atomCache().all(); !r.empty(); r.popFront()) {
     455           0 :         JSAtom* atom = r.front().asPtrUnbarriered();
     456           0 :         TraceRoot(trc, &atom, "kept atom");
     457           0 :         MOZ_ASSERT(r.front().asPtrUnbarriered() == atom);
     458             :     }
     459           0 : }
     460             : 
     461           0 : ZoneList::ZoneList()
     462           8 :   : head(nullptr), tail(nullptr)
     463           8 : {}
     464             : 
     465           0 : ZoneList::ZoneList(Zone* zone)
     466           0 :   : head(zone), tail(zone)
     467             : {
     468           0 :     MOZ_RELEASE_ASSERT(!zone->isOnList());
     469           0 :     zone->listNext_ = nullptr;
     470           0 : }
     471             : 
     472           0 : ZoneList::~ZoneList()
     473             : {
     474           0 :     MOZ_ASSERT(isEmpty());
     475           0 : }
     476             : 
     477             : void
     478           0 : ZoneList::check() const
     479             : {
     480             : #ifdef DEBUG
     481           0 :     MOZ_ASSERT((head == nullptr) == (tail == nullptr));
     482           0 :     if (!head)
     483             :         return;
     484             : 
     485             :     Zone* zone = head;
     486           0 :     for (;;) {
     487           0 :         MOZ_ASSERT(zone && zone->isOnList());
     488           0 :         if  (zone == tail)
     489             :             break;
     490           0 :         zone = zone->listNext_;
     491             :     }
     492           0 :     MOZ_ASSERT(!zone->listNext_);
     493             : #endif
     494             : }
     495             : 
     496             : bool
     497           0 : ZoneList::isEmpty() const
     498             : {
     499           0 :     return head == nullptr;
     500             : }
     501             : 
     502             : Zone*
     503           0 : ZoneList::front() const
     504             : {
     505           0 :     MOZ_ASSERT(!isEmpty());
     506           0 :     MOZ_ASSERT(head->isOnList());
     507           0 :     return head;
     508             : }
     509             : 
     510             : void
     511           0 : ZoneList::append(Zone* zone)
     512             : {
     513           0 :     ZoneList singleZone(zone);
     514           0 :     transferFrom(singleZone);
     515           0 : }
     516             : 
     517             : void
     518           0 : ZoneList::transferFrom(ZoneList& other)
     519             : {
     520           0 :     check();
     521           0 :     other.check();
     522           0 :     MOZ_ASSERT(tail != other.tail);
     523             : 
     524             :     if (tail)
     525             :         tail->listNext_ = other.head;
     526             :     else
     527             :         head = other.head;
     528             :     tail = other.tail;
     529             : 
     530             :     other.head = nullptr;
     531             :     other.tail = nullptr;
     532             : }
     533             : 
     534             : Zone*
     535             : ZoneList::removeFront()
     536             : {
     537             :     MOZ_ASSERT(!isEmpty());
     538             :     check();
     539             : 
     540             :     Zone* front = head;
     541             :     head = head->listNext_;
     542             :     if (!head)
     543             :         tail = nullptr;
     544             : 
     545             :     front->listNext_ = Zone::NotOnList;
     546             : 
     547             :     return front;
     548             : }
     549             : 
     550             : void
     551             : ZoneList::clear()
     552             : {
     553             :     while (!isEmpty())
     554             :         removeFront();
     555             : }
     556             : 
     557             : JS_PUBLIC_API(void)
     558             : JS::shadow::RegisterWeakCache(JS::Zone* zone, detail::WeakCacheBase* cachep)
     559             : {
     560             :     zone->registerWeakCache(cachep);
     561             : }

Generated by: LCOV version 1.13-14-ga5dd952