diff options
Diffstat (limited to 'js/public/TracingAPI.h')
-rw-r--r-- | js/public/TracingAPI.h | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h new file mode 100644 index 000000000..37c69acad --- /dev/null +++ b/js/public/TracingAPI.h @@ -0,0 +1,403 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 <typename T> class Heap; +template <typename T> 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 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)); + } + + // 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(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); } + + 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<JS::CallbackTracer*>(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 <typename T> +extern JS_PUBLIC_API(void) +TraceEdge(JSTracer* trc, JS::Heap<T>* edgep, const char* name); + +extern JS_PUBLIC_API(void) +TraceEdge(JSTracer* trc, JS::TenuredHeap<JSObject*>* 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 <typename T> +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<Zone*, js::DefaultHasher<Zone*>, js::SystemAllocPolicy>; +using CompartmentSet = js::HashSet<JSCompartment*, js::DefaultHasher<JSCompartment*>, + 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 <typename T> +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 <typename T> +extern JS_PUBLIC_API(bool) +EdgeNeedsSweep(JS::Heap<T>* edgep); + +// Not part of the public API, but declared here so we can use it in GCPolicy +// which is. +template <typename T> +bool +IsAboutToBeFinalizedUnbarriered(T* thingp); + +} // namespace gc +} // namespace js + +#endif /* js_TracingAPI_h */ |