/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef js_TracingAPI_h #define js_TracingAPI_h #include "jsalloc.h" #include "js/HashTable.h" #include "js/HeapAPI.h" #include "js/TraceKind.h" class JS_PUBLIC_API(JSTracer); namespace JS { class JS_PUBLIC_API(CallbackTracer); template class Heap; template class TenuredHeap; /** Returns a static string equivalent of |kind|. */ JS_FRIEND_API(const char*) GCTraceKindToAscii(JS::TraceKind kind); } // namespace JS enum WeakMapTraceKind { /** * Do not trace into weak map keys or values during traversal. Users must * handle weak maps manually. */ DoNotTraceWeakMaps, /** * Do true ephemeron marking with a weak key lookup marking phase. This is * the default for GCMarker. */ ExpandWeakMaps, /** * Trace through to all values, irrespective of whether the keys are live * or not. Used for non-marking tracers. */ TraceWeakMapValues, /** * Trace through to all keys and values, irrespective of whether the keys * are live or not. Used for non-marking tracers. */ TraceWeakMapKeysValues }; class JS_PUBLIC_API(JSTracer) { public: // Return the runtime set on the tracer. JSRuntime* runtime() const { return runtime_; } // Return the weak map tracing behavior currently set on this tracer. WeakMapTraceKind weakMapAction() const { return weakMapAction_; } enum class TracerKindTag { // Marking path: a tracer used only for marking liveness of cells, not // for moving them. The kind will transition to WeakMarking after // everything reachable by regular edges has been marked. Marking, // Same as Marking, except we have now moved on to the "weak marking // phase", in which every marked obj/script is immediately looked up to // see if it is a weak map key (and therefore might require marking its // weak map value). WeakMarking, // A tracer that traverses the graph for the purposes of moving objects // from the nursery to the tenured area. Tenuring, // General-purpose traversal that invokes a callback on each cell. // Traversing children is the responsibility of the callback. Callback }; bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; } bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; } bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; } bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; } inline JS::CallbackTracer* asCallbackTracer(); #ifdef DEBUG bool checkEdges() { return checkEdges_; } #endif protected: JSTracer(JSRuntime* rt, TracerKindTag tag, WeakMapTraceKind weakTraceKind = TraceWeakMapValues) : runtime_(rt) , weakMapAction_(weakTraceKind) #ifdef DEBUG , checkEdges_(true) #endif , tag_(tag) {} #ifdef DEBUG // Set whether to check edges are valid in debug builds. void setCheckEdges(bool check) { checkEdges_ = check; } #endif private: JSRuntime* runtime_; WeakMapTraceKind weakMapAction_; #ifdef DEBUG bool checkEdges_; #endif protected: TracerKindTag tag_; }; namespace JS { class AutoTracingName; class AutoTracingIndex; class AutoTracingCallback; class JS_PUBLIC_API(CallbackTracer) : public JSTracer { public: CallbackTracer(JSRuntime* rt, WeakMapTraceKind weakTraceKind = TraceWeakMapValues) : JSTracer(rt, JSTracer::TracerKindTag::Callback, weakTraceKind), contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr) {} CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind = TraceWeakMapValues); // Override these methods to receive notification when an edge is visited // with the type contained in the callback. The default implementation // dispatches to the fully-generic onChild implementation, so for cases that // do not care about boxing overhead and do not need the actual edges, // just override the generic onChild. virtual void onObjectEdge(JSObject** objp) { onChild(JS::GCCellPtr(*objp)); } virtual void onStringEdge(JSString** strp) { onChild(JS::GCCellPtr(*strp)); } virtual void onSymbolEdge(JS::Symbol** symp) { onChild(JS::GCCellPtr(*symp)); } virtual void onBigIntEdge(JS::BigInt** bip) { onChild(JS::GCCellPtr(*bip)); } virtual void onScriptEdge(JSScript** scriptp) { onChild(JS::GCCellPtr(*scriptp)); } virtual void onShapeEdge(js::Shape** shapep) { onChild(JS::GCCellPtr(*shapep, JS::TraceKind::Shape)); } virtual void onObjectGroupEdge(js::ObjectGroup** groupp) { onChild(JS::GCCellPtr(*groupp, JS::TraceKind::ObjectGroup)); } virtual void onBaseShapeEdge(js::BaseShape** basep) { onChild(JS::GCCellPtr(*basep, JS::TraceKind::BaseShape)); } virtual void onJitCodeEdge(js::jit::JitCode** codep) { onChild(JS::GCCellPtr(*codep, JS::TraceKind::JitCode)); } virtual void onLazyScriptEdge(js::LazyScript** lazyp) { onChild(JS::GCCellPtr(*lazyp, JS::TraceKind::LazyScript)); } virtual void onScopeEdge(js::Scope** scopep) { onChild(JS::GCCellPtr(*scopep, JS::TraceKind::Scope)); } virtual void onRegExpSharedEdge(js::RegExpShared** sharedp) { onChild(JS::GCCellPtr(*sharedp, JS::TraceKind::RegExpShared)); } // Override this method to receive notification when a node in the GC // heap graph is visited. virtual void onChild(const JS::GCCellPtr& thing) = 0; // Access to the tracing context: // When tracing with a JS::CallbackTracer, we invoke the callback with the // edge location and the type of target. This is useful for operating on // the edge in the abstract or on the target thing, satisfying most common // use cases. However, some tracers need additional detail about the // specific edge that is being traced in order to be useful. Unfortunately, // the raw pointer to the edge that we provide is not enough information to // infer much of anything useful about that edge. // // In order to better support use cases that care in particular about edges // -- as opposed to the target thing -- tracing implementations are // responsible for providing extra context information about each edge they // trace, as it is traced. This contains, at a minimum, an edge name and, // when tracing an array, the index. Further specialization can be achived // (with some complexity), by associating a functor with the tracer so // that, when requested, the user can generate totally custom edge // descriptions. // Returns the current edge's name. It is only valid to call this when // inside the trace callback, however, the edge name will always be set. const char* contextName() const { MOZ_ASSERT(contextName_); return contextName_; } // Returns the current edge's index, if marked as part of an array of edges. // This must be called only inside the trace callback. When not tracing an // array, the value will be InvalidIndex. const static size_t InvalidIndex = size_t(-1); size_t contextIndex() const { return contextIndex_; } // Build a description of this edge in the heap graph. This call may invoke // the context functor, if set, which may inspect arbitrary areas of the // heap. On the other hand, the description provided by this method may be // substantially more accurate and useful than those provided by only the // contextName and contextIndex. void getTracingEdgeName(char* buffer, size_t bufferSize); // The trace implementation may associate a callback with one or more edges // using AutoTracingDetails. This functor is called by getTracingEdgeName // and is responsible for providing a textual representation of the // currently being traced edge. The callback has access to the full heap, // including the currently set tracing context. class ContextFunctor { public: virtual void operator()(CallbackTracer* trc, char* buf, size_t bufsize) = 0; }; #ifdef DEBUG enum class TracerKind { DoNotCare, Moving, GrayBuffering, VerifyTraceProtoAndIface }; virtual TracerKind getTracerKind() const { return TracerKind::DoNotCare; } #endif // In C++, overriding a method hides all methods in the base class with // that name, not just methods with that signature. Thus, the typed edge // methods have to have distinct names to allow us to override them // individually, which is freqently useful if, for example, we only want to // process only one type of edge. void dispatchToOnEdge(JSObject** objp) { onObjectEdge(objp); } void dispatchToOnEdge(JSString** strp) { onStringEdge(strp); } void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); } void dispatchToOnEdge(JS::BigInt** bip) { onBigIntEdge(bip); } void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); } void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); } void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); } void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); } void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); } void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); } void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); } void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); } private: friend class AutoTracingName; const char* contextName_; friend class AutoTracingIndex; size_t contextIndex_; friend class AutoTracingDetails; ContextFunctor* contextFunctor_; }; // Set the name portion of the tracer's context for the current edge. class MOZ_RAII AutoTracingName { CallbackTracer* trc_; const char* prior_; public: AutoTracingName(CallbackTracer* trc, const char* name) : trc_(trc), prior_(trc->contextName_) { MOZ_ASSERT(name); trc->contextName_ = name; } ~AutoTracingName() { MOZ_ASSERT(trc_->contextName_); trc_->contextName_ = prior_; } }; // Set the index portion of the tracer's context for the current range. class MOZ_RAII AutoTracingIndex { CallbackTracer* trc_; public: explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(nullptr) { if (trc->isCallbackTracer()) { trc_ = trc->asCallbackTracer(); MOZ_ASSERT(trc_->contextIndex_ == CallbackTracer::InvalidIndex); trc_->contextIndex_ = initial; } } ~AutoTracingIndex() { if (trc_) { MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex); trc_->contextIndex_ = CallbackTracer::InvalidIndex; } } void operator++() { if (trc_) { MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex); ++trc_->contextIndex_; } } }; // Set a context callback for the trace callback to use, if it needs a detailed // edge description. class MOZ_RAII AutoTracingDetails { CallbackTracer* trc_; public: AutoTracingDetails(JSTracer* trc, CallbackTracer::ContextFunctor& func) : trc_(nullptr) { if (trc->isCallbackTracer()) { trc_ = trc->asCallbackTracer(); MOZ_ASSERT(trc_->contextFunctor_ == nullptr); trc_->contextFunctor_ = &func; } } ~AutoTracingDetails() { if (trc_) { MOZ_ASSERT(trc_->contextFunctor_); trc_->contextFunctor_ = nullptr; } } }; } // namespace JS JS::CallbackTracer* JSTracer::asCallbackTracer() { MOZ_ASSERT(isCallbackTracer()); return static_cast(this); } namespace JS { // The JS::TraceEdge family of functions traces the given GC thing reference. // This performs the tracing action configured on the given JSTracer: typically // calling the JSTracer::callback or marking the thing as live. // // The argument to JS::TraceEdge is an in-out param: when the function returns, // the garbage collector might have moved the GC thing. In this case, the // reference passed to JS::TraceEdge will be updated to the thing's new // location. Callers of this method are responsible for updating any state that // is dependent on the object's address. For example, if the object's address // is used as a key in a hashtable, then the object must be removed and // re-inserted with the correct hash. // // Note that while |edgep| must never be null, it is fine for |*edgep| to be // nullptr. template extern JS_PUBLIC_API(void) TraceEdge(JSTracer* trc, JS::Heap* edgep, const char* name); extern JS_PUBLIC_API(void) TraceEdge(JSTracer* trc, JS::TenuredHeap* edgep, const char* name); // Edges that are always traced as part of root marking do not require // incremental barriers. This function allows for marking non-barriered // pointers, but asserts that this happens during root marking. // // Note that while |edgep| must never be null, it is fine for |*edgep| to be // nullptr. template extern JS_PUBLIC_API(void) UnsafeTraceRoot(JSTracer* trc, T* edgep, const char* name); extern JS_PUBLIC_API(void) TraceChildren(JSTracer* trc, GCCellPtr thing); using ZoneSet = js::HashSet, js::SystemAllocPolicy>; using CompartmentSet = js::HashSet, js::SystemAllocPolicy>; /** * Trace every value within |compartments| that is wrapped by a * cross-compartment wrapper from a compartment that is not an element of * |compartments|. */ extern JS_PUBLIC_API(void) TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments); } // namespace JS extern JS_PUBLIC_API(void) JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, JS::TraceKind kind, bool includeDetails); namespace js { // Trace an edge that is not a GC root and is not wrapped in a barriered // wrapper for some reason. // // This method does not check if |*edgep| is non-null before tracing through // it, so callers must check any nullable pointer before calling this method. template extern JS_PUBLIC_API(void) UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name); namespace gc { // Return true if the given edge is not live and is about to be swept. template extern JS_PUBLIC_API(bool) EdgeNeedsSweep(JS::Heap* edgep); // Not part of the public API, but declared here so we can use it in GCPolicy // which is. template bool IsAboutToBeFinalizedUnbarriered(T* thingp); } // namespace gc } // namespace js #endif /* js_TracingAPI_h */