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

Generated by: LCOV version 1.13