LCOV - code coverage report
Current view: top level - js/src/gc - Tracer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 25 212 11.8 %
Date: 2017-07-14 16:53:18 Functions: 14 56 25.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/Tracer.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/SizePrintfMacros.h"
      11             : 
      12             : #include "jsapi.h"
      13             : #include "jsfun.h"
      14             : #include "jsgc.h"
      15             : #include "jsprf.h"
      16             : #include "jsscript.h"
      17             : #include "jsutil.h"
      18             : #include "NamespaceImports.h"
      19             : 
      20             : #include "gc/GCInternals.h"
      21             : #include "gc/Marking.h"
      22             : #include "gc/Zone.h"
      23             : 
      24             : #include "vm/Shape.h"
      25             : #include "vm/Symbol.h"
      26             : 
      27             : #include "jscompartmentinlines.h"
      28             : #include "jsgcinlines.h"
      29             : 
      30             : #include "vm/ObjectGroup-inl.h"
      31             : 
      32             : using namespace js;
      33             : using namespace js::gc;
      34             : using mozilla::DebugOnly;
      35             : 
      36             : namespace js {
      37             : template<typename T>
      38             : void
      39             : CheckTracedThing(JSTracer* trc, T thing);
      40             : } // namespace js
      41             : 
      42             : 
      43             : /*** Callback Tracer Dispatch ********************************************************************/
      44             : 
      45             : template <typename T>
      46             : T
      47        5202 : DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name)
      48             : {
      49        5202 :     CheckTracedThing(trc, *thingp);
      50       10404 :     JS::AutoTracingName ctx(trc, name);
      51        5202 :     trc->dispatchToOnEdge(thingp);
      52       10404 :     return *thingp;
      53             : }
      54             : #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(name, type, _) \
      55             :     template type* DoCallback<type*>(JS::CallbackTracer*, type**, const char*);
      56             : JS_FOR_EACH_TRACEKIND(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
      57             : #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
      58             : 
      59             : template <typename S>
      60             : struct DoCallbackFunctor : public IdentityDefaultAdaptor<S> {
      61          57 :     template <typename T> S operator()(T* t, JS::CallbackTracer* trc, const char* name) {
      62          57 :         return js::gc::RewrapTaggedPointer<S, T>::wrap(DoCallback(trc, &t, name));
      63             :     }
      64             : };
      65             : 
      66             : template <>
      67             : Value
      68        3907 : DoCallback<Value>(JS::CallbackTracer* trc, Value* vp, const char* name)
      69             : {
      70        3907 :     *vp = DispatchTyped(DoCallbackFunctor<Value>(), *vp, trc, name);
      71        3907 :     return *vp;
      72             : }
      73             : 
      74             : template <>
      75             : jsid
      76           0 : DoCallback<jsid>(JS::CallbackTracer* trc, jsid* idp, const char* name)
      77             : {
      78           0 :     *idp = DispatchTyped(DoCallbackFunctor<jsid>(), *idp, trc, name);
      79           0 :     return *idp;
      80             : }
      81             : 
      82             : template <>
      83             : TaggedProto
      84           0 : DoCallback<TaggedProto>(JS::CallbackTracer* trc, TaggedProto* protop, const char* name)
      85             : {
      86           0 :     *protop = DispatchTyped(DoCallbackFunctor<TaggedProto>(), *protop, trc, name);
      87           0 :     return *protop;
      88             : }
      89             : 
      90             : void
      91           0 : JS::CallbackTracer::getTracingEdgeName(char* buffer, size_t bufferSize)
      92             : {
      93           0 :     MOZ_ASSERT(bufferSize > 0);
      94           0 :     if (contextFunctor_) {
      95           0 :         (*contextFunctor_)(this, buffer, bufferSize);
      96           0 :         return;
      97             :     }
      98           0 :     if (contextIndex_ != InvalidIndex) {
      99           0 :         snprintf(buffer, bufferSize, "%s[%" PRIuSIZE "]", contextName_, contextIndex_);
     100           0 :         return;
     101             :     }
     102           0 :     snprintf(buffer, bufferSize, "%s", contextName_);
     103             : }
     104             : 
     105             : 
     106             : /*** Public Tracing API **************************************************************************/
     107             : 
     108             : JS_PUBLIC_API(void)
     109          25 : JS::TraceChildren(JSTracer* trc, GCCellPtr thing)
     110             : {
     111          25 :     js::TraceChildren(trc, thing.asCell(), thing.kind());
     112          25 : }
     113             : 
     114             : struct TraceChildrenFunctor {
     115             :     template <typename T>
     116          25 :     void operator()(JSTracer* trc, void* thingArg) {
     117          25 :         T* thing = static_cast<T*>(thingArg);
     118          25 :         MOZ_ASSERT_IF(thing->runtimeFromAnyThread() != trc->runtime(),
     119             :             ThingIsPermanentAtomOrWellKnownSymbol(thing) ||
     120             :             thing->zoneFromAnyThread()->isSelfHostingZone());
     121             : 
     122          25 :         thing->traceChildren(trc);
     123          25 :     }
     124             : };
     125             : 
     126             : void
     127          25 : js::TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind)
     128             : {
     129          25 :     MOZ_ASSERT(thing);
     130             :     TraceChildrenFunctor f;
     131          25 :     DispatchTraceKindTyped(f, kind, trc, thing);
     132          25 : }
     133             : 
     134             : namespace {
     135             : struct TraceIncomingFunctor {
     136             :     JSTracer* trc_;
     137             :     const JS::CompartmentSet& compartments_;
     138           0 :     TraceIncomingFunctor(JSTracer* trc, const JS::CompartmentSet& compartments)
     139           0 :       : trc_(trc), compartments_(compartments)
     140           0 :     {}
     141             :     template <typename T>
     142           0 :     void operator()(T tp) {
     143           0 :         if (!compartments_.has((*tp)->compartment()))
     144           0 :             return;
     145           0 :         TraceManuallyBarrieredEdge(trc_, tp, "cross-compartment wrapper");
     146             :     }
     147             :     // StringWrappers are just used to avoid copying strings
     148             :     // across zones multiple times, and don't hold a strong
     149             :     // reference.
     150           0 :     void operator()(JSString** tp) {}
     151             : };
     152             : } // namespace (anonymous)
     153             : 
     154             : JS_PUBLIC_API(void)
     155           0 : JS::TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments)
     156             : {
     157           0 :     for (js::CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
     158           0 :         if (compartments.has(comp))
     159           0 :             continue;
     160             : 
     161           0 :         for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
     162           0 :             mozilla::DebugOnly<const CrossCompartmentKey> prior = e.front().key();
     163           0 :             e.front().mutableKey().applyToWrapped(TraceIncomingFunctor(trc, compartments));
     164           0 :             MOZ_ASSERT(e.front().key() == prior);
     165             :         }
     166             :     }
     167           0 : }
     168             : 
     169             : 
     170             : /*** Cycle Collector Helpers **********************************************************************/
     171             : 
     172             : // This function is used by the Cycle Collector (CC) to trace through -- or in
     173             : // CC parlance, traverse -- a Shape tree. The CC does not care about Shapes or
     174             : // BaseShapes, only the JSObjects held live by them. Thus, we walk the Shape
     175             : // lineage, but only report non-Shape things. This effectively makes the entire
     176             : // shape lineage into a single node in the CC, saving tremendous amounts of
     177             : // space and time in its algorithms.
     178             : //
     179             : // The algorithm implemented here uses only bounded stack space. This would be
     180             : // possible to implement outside the engine, but would require much extra
     181             : // infrastructure and many, many more slow GOT lookups. We have implemented it
     182             : // inside SpiderMonkey, despite the lack of general applicability, for the
     183             : // simplicity and performance of FireFox's embedding of this engine.
     184             : void
     185           0 : gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape)
     186             : {
     187           0 :     do {
     188           0 :         MOZ_ASSERT(shape->base());
     189           0 :         shape->base()->assertConsistency();
     190             : 
     191           0 :         TraceEdge(trc, &shape->propidRef(), "propid");
     192             : 
     193           0 :         if (shape->hasGetterObject()) {
     194           0 :             JSObject* tmp = shape->getterObject();
     195           0 :             DoCallback(trc, &tmp, "getter");
     196           0 :             MOZ_ASSERT(tmp == shape->getterObject());
     197             :         }
     198             : 
     199           0 :         if (shape->hasSetterObject()) {
     200           0 :             JSObject* tmp = shape->setterObject();
     201           0 :             DoCallback(trc, &tmp, "setter");
     202           0 :             MOZ_ASSERT(tmp == shape->setterObject());
     203             :         }
     204             : 
     205           0 :         shape = shape->previous();
     206           0 :     } while (shape);
     207           0 : }
     208             : 
     209             : // Object groups can point to other object groups via an UnboxedLayout or the
     210             : // the original unboxed group link. There can potentially be deep or cyclic
     211             : // chains of such groups to trace through without going through a thing that
     212             : // participates in cycle collection. These need to be handled iteratively to
     213             : // avoid blowing the stack when running the cycle collector's callback tracer.
     214           0 : struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer
     215             : {
     216           0 :     explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer)
     217           0 :         : JS::CallbackTracer(innerTracer->runtime(), DoNotTraceWeakMaps),
     218           0 :           innerTracer(innerTracer)
     219           0 :     {}
     220             : 
     221             :     void onChild(const JS::GCCellPtr& thing) override;
     222             : 
     223             :     JS::CallbackTracer* innerTracer;
     224             :     Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist;
     225             : };
     226             : 
     227             : void
     228           0 : ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
     229             : {
     230           0 :     if (thing.is<BaseShape>()) {
     231             :         // The CC does not care about BaseShapes, and no additional GC things
     232             :         // will be reached by following this edge.
     233           0 :         return;
     234             :     }
     235             : 
     236           0 :     if (thing.is<JSObject>() || thing.is<JSScript>()) {
     237             :         // Invoke the inner cycle collector callback on this child. It will not
     238             :         // recurse back into TraceChildren.
     239           0 :         innerTracer->onChild(thing);
     240           0 :         return;
     241             :     }
     242             : 
     243           0 :     if (thing.is<ObjectGroup>()) {
     244             :         // If this group is required to be in an ObjectGroup chain, trace it
     245             :         // via the provided worklist rather than continuing to recurse.
     246           0 :         ObjectGroup& group = thing.as<ObjectGroup>();
     247           0 :         if (group.maybeUnboxedLayout()) {
     248           0 :             for (size_t i = 0; i < seen.length(); i++) {
     249           0 :                 if (seen[i] == &group)
     250           0 :                     return;
     251             :             }
     252           0 :             if (seen.append(&group) && worklist.append(&group)) {
     253           0 :                 return;
     254             :             } else {
     255             :                 // If append fails, keep tracing normally. The worst that will
     256             :                 // happen is we end up overrecursing.
     257             :             }
     258             :         }
     259             :     }
     260             : 
     261           0 :     TraceChildren(this, thing.asCell(), thing.kind());
     262             : }
     263             : 
     264             : void
     265           0 : gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group)
     266             : {
     267           0 :     MOZ_ASSERT(trc->isCallbackTracer());
     268             : 
     269             :     // Early return if this group is not required to be in an ObjectGroup chain.
     270           0 :     if (!group->maybeUnboxedLayout())
     271           0 :         return group->traceChildren(trc);
     272             : 
     273           0 :     ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer());
     274           0 :     group->traceChildren(&groupTracer);
     275             : 
     276           0 :     while (!groupTracer.worklist.empty()) {
     277           0 :         ObjectGroup* innerGroup = groupTracer.worklist.popCopy();
     278           0 :         innerGroup->traceChildren(&groupTracer);
     279             :     }
     280             : }
     281             : 
     282             : 
     283             : /*** Traced Edge Printer *************************************************************************/
     284             : 
     285             : static size_t
     286           0 : CountDecimalDigits(size_t num)
     287             : {
     288           0 :     size_t numDigits = 0;
     289           0 :     do {
     290           0 :         num /= 10;
     291           0 :         numDigits++;
     292           0 :     } while (num > 0);
     293             : 
     294           0 :     return numDigits;
     295             : }
     296             : 
     297             : static const char*
     298           0 : StringKindHeader(JSString* str)
     299             : {
     300           0 :     MOZ_ASSERT(str->isLinear());
     301             : 
     302           0 :     if (str->isAtom()) {
     303           0 :         if (str->isPermanentAtom())
     304           0 :             return "permanent atom: ";
     305           0 :         return "atom: ";
     306             :     }
     307             : 
     308           0 :     if (str->isFlat()) {
     309           0 :         if (str->isExtensible())
     310           0 :             return "extensible: ";
     311           0 :         if (str->isUndepended())
     312           0 :             return "undepended: ";
     313           0 :         if (str->isInline()) {
     314           0 :             if (str->isFatInline())
     315           0 :                 return "fat inline: ";
     316           0 :             return "inline: ";
     317             :         }
     318           0 :         return "flat: ";
     319             :     }
     320             : 
     321           0 :     if (str->isDependent())
     322           0 :         return "dependent: ";
     323           0 :     if (str->isExternal())
     324           0 :         return "external: ";
     325           0 :     return "linear: ";
     326             : }
     327             : 
     328             : JS_PUBLIC_API(void)
     329           0 : JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing,
     330             :                      JS::TraceKind kind, bool details)
     331             : {
     332           0 :     const char* name = nullptr; /* silence uninitialized warning */
     333             :     size_t n;
     334             : 
     335           0 :     if (bufsize == 0)
     336           0 :         return;
     337             : 
     338           0 :     switch (kind) {
     339             :       case JS::TraceKind::BaseShape:
     340           0 :         name = "base_shape";
     341           0 :         break;
     342             : 
     343             :       case JS::TraceKind::JitCode:
     344           0 :         name = "jitcode";
     345           0 :         break;
     346             : 
     347             :       case JS::TraceKind::LazyScript:
     348           0 :         name = "lazyscript";
     349           0 :         break;
     350             : 
     351             :       case JS::TraceKind::Null:
     352           0 :         name = "null_pointer";
     353           0 :         break;
     354             : 
     355             :       case JS::TraceKind::Object:
     356             :       {
     357           0 :         name = static_cast<JSObject*>(thing)->getClass()->name;
     358           0 :         break;
     359             :       }
     360             : 
     361             :       case JS::TraceKind::ObjectGroup:
     362           0 :         name = "object_group";
     363           0 :         break;
     364             : 
     365             :       case JS::TraceKind::RegExpShared:
     366           0 :         name = "reg_exp_shared";
     367           0 :         break;
     368             : 
     369             :       case JS::TraceKind::Scope:
     370           0 :         name = "scope";
     371           0 :         break;
     372             : 
     373             :       case JS::TraceKind::Script:
     374           0 :         name = "script";
     375           0 :         break;
     376             : 
     377             :       case JS::TraceKind::Shape:
     378           0 :         name = "shape";
     379           0 :         break;
     380             : 
     381             :       case JS::TraceKind::String:
     382           0 :         name = ((JSString*)thing)->isDependent()
     383           0 :                ? "substring"
     384             :                : "string";
     385           0 :         break;
     386             : 
     387             :       case JS::TraceKind::Symbol:
     388           0 :         name = "symbol";
     389           0 :         break;
     390             : 
     391             :       default:
     392           0 :         name = "INVALID";
     393           0 :         break;
     394             :     }
     395             : 
     396           0 :     n = strlen(name);
     397           0 :     if (n > bufsize - 1)
     398           0 :         n = bufsize - 1;
     399           0 :     js_memcpy(buf, name, n + 1);
     400           0 :     buf += n;
     401           0 :     bufsize -= n;
     402           0 :     *buf = '\0';
     403             : 
     404           0 :     if (details && bufsize > 2) {
     405           0 :         switch (kind) {
     406             :           case JS::TraceKind::Object:
     407             :           {
     408           0 :             JSObject* obj = (JSObject*)thing;
     409           0 :             if (obj->is<JSFunction>()) {
     410           0 :                 JSFunction* fun = &obj->as<JSFunction>();
     411           0 :                 if (fun->displayAtom()) {
     412           0 :                     *buf++ = ' ';
     413           0 :                     bufsize--;
     414           0 :                     PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
     415             :                 }
     416           0 :             } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
     417           0 :                 snprintf(buf, bufsize, " %p", obj->as<NativeObject>().getPrivate());
     418             :             } else {
     419           0 :                 snprintf(buf, bufsize, " <no private>");
     420             :             }
     421           0 :             break;
     422             :           }
     423             : 
     424             :           case JS::TraceKind::Script:
     425             :           {
     426           0 :             JSScript* script = static_cast<JSScript*>(thing);
     427           0 :             snprintf(buf, bufsize, " %s:%" PRIuSIZE, script->filename(), script->lineno());
     428           0 :             break;
     429             :           }
     430             : 
     431             :           case JS::TraceKind::String:
     432             :           {
     433           0 :             *buf++ = ' ';
     434           0 :             bufsize--;
     435           0 :             JSString* str = (JSString*)thing;
     436             : 
     437           0 :             if (str->isLinear()) {
     438           0 :                 const char* header = StringKindHeader(str);
     439           0 :                 bool willFit = str->length() + strlen("<length > ") + strlen(header) +
     440           0 :                                CountDecimalDigits(str->length()) < bufsize;
     441             : 
     442           0 :                 n = snprintf(buf, bufsize, "<%slength %" PRIuSIZE "%s> ",
     443             :                              header, str->length(),
     444             :                              willFit ? "" : " (truncated)");
     445           0 :                 buf += n;
     446           0 :                 bufsize -= n;
     447             : 
     448           0 :                 PutEscapedString(buf, bufsize, &str->asLinear(), 0);
     449             :             } else {
     450           0 :                 snprintf(buf, bufsize, "<rope: length %" PRIuSIZE ">", str->length());
     451             :             }
     452           0 :             break;
     453             :           }
     454             : 
     455             :           case JS::TraceKind::Symbol:
     456             :           {
     457           0 :             JS::Symbol* sym = static_cast<JS::Symbol*>(thing);
     458           0 :             if (JSString* desc = sym->description()) {
     459           0 :                 if (desc->isLinear()) {
     460           0 :                     *buf++ = ' ';
     461           0 :                     bufsize--;
     462           0 :                     PutEscapedString(buf, bufsize, &desc->asLinear(), 0);
     463             :                 } else {
     464           0 :                     snprintf(buf, bufsize, "<nonlinear desc>");
     465             :                 }
     466             :             } else {
     467           0 :                 snprintf(buf, bufsize, "<null>");
     468             :             }
     469           0 :             break;
     470             :           }
     471             : 
     472             :           case JS::TraceKind::Scope:
     473             :           {
     474           0 :             js::Scope* scope = static_cast<js::Scope*>(thing);
     475           0 :             snprintf(buf, bufsize, " %s", js::ScopeKindString(scope->kind()));
     476           0 :             break;
     477             :           }
     478             : 
     479             :           default:
     480           0 :             break;
     481             :         }
     482             :     }
     483           0 :     buf[bufsize - 1] = '\0';
     484             : }
     485             : 
     486          25 : JS::CallbackTracer::CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind)
     487          25 :   : CallbackTracer(cx->runtime(), weakTraceKind)
     488          25 : {}

Generated by: LCOV version 1.13