summaryrefslogtreecommitdiffstats
path: root/js/public/TracingAPI.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/public/TracingAPI.h')
-rw-r--r--js/public/TracingAPI.h421
1 files changed, 421 insertions, 0 deletions
diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h
new file mode 100644
index 0000000000..9c69eec160
--- /dev/null
+++ b/js/public/TracingAPI.h
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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 "js/GCTypeMacros.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_PUBLIC_API const char* GCTraceKindToAscii(JS::TraceKind kind);
+
+/** Returns the base size in bytes of the GC thing of kind |kind|. */
+JS_PUBLIC_API size_t GCTraceKindSize(JS::TraceKind kind);
+
+// Kinds of JSTracer.
+enum class TracerKind {
+ // Generic tracers: Internal tracers that have a different virtual method
+ // called for each edge kind.
+ Marking,
+ Tenuring,
+ Moving,
+ ClearEdges,
+ Sweeping,
+ MinorSweeping,
+ Barrier,
+
+ // Callback tracers: General-purpose tracers that have a single virtual
+ // method called on every edge.
+ //
+ // Order is important. All callback kinds must follow this one.
+ Callback,
+
+ // Specific kinds of callback tracer.
+ UnmarkGray,
+ VerifyTraceProtoAndIface,
+ CompartmentCheck,
+};
+
+enum class WeakMapTraceAction {
+ /**
+ * Do not trace into weak map keys or values during traversal. Users must
+ * handle weak maps manually.
+ */
+ Skip,
+
+ /**
+ * Do true ephemeron marking with a weak key lookup marking phase. This is
+ * the default for GCMarker.
+ */
+ Expand,
+
+ /**
+ * Trace through to all values, irrespective of whether the keys are live
+ * or not. Used for non-marking tracers.
+ */
+ TraceValues,
+
+ /**
+ * Trace through to all keys and values, irrespective of whether the keys
+ * are live or not. Used for non-marking tracers.
+ */
+ TraceKeysAndValues
+};
+
+// Whether a tracer should trace weak edges. GCMarker sets this to Skip.
+enum class WeakEdgeTraceAction { Skip, Trace };
+
+struct TraceOptions {
+ JS::WeakMapTraceAction weakMapAction = WeakMapTraceAction::TraceValues;
+ JS::WeakEdgeTraceAction weakEdgeAction = WeakEdgeTraceAction::Trace;
+
+ TraceOptions() = default;
+ TraceOptions(JS::WeakMapTraceAction weakMapActionArg,
+ JS::WeakEdgeTraceAction weakEdgeActionArg)
+ : weakMapAction(weakMapActionArg), weakEdgeAction(weakEdgeActionArg) {}
+ MOZ_IMPLICIT TraceOptions(JS::WeakMapTraceAction weakMapActionArg)
+ : weakMapAction(weakMapActionArg) {}
+ MOZ_IMPLICIT TraceOptions(JS::WeakEdgeTraceAction weakEdgeActionArg)
+ : weakEdgeAction(weakEdgeActionArg) {}
+};
+
+class AutoTracingIndex;
+
+// Optional context information that can be used to construct human readable
+// descriptions of what is being traced.
+class TracingContext {
+ public:
+ // 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 achieved (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 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.
+ constexpr static size_t InvalidIndex = size_t(-1);
+ size_t index() const { return index_; }
+
+ // 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
+ // name and index.
+ void getEdgeName(const char* name, char* buffer, size_t bufferSize);
+
+ // The trace implementation may associate a callback with one or more edges
+ // using AutoTracingDetails. This functor is called by getEdgeName and
+ // is responsible for providing a textual representation of the edge currently
+ // being traced. The callback has access to the full heap, including the
+ // currently set tracing context.
+ class Functor {
+ public:
+ virtual void operator()(TracingContext* tcx, char* buf, size_t bufsize) = 0;
+ };
+
+ private:
+ friend class AutoTracingIndex;
+ size_t index_ = InvalidIndex;
+
+ friend class AutoTracingDetails;
+ Functor* functor_ = nullptr;
+};
+
+} // namespace JS
+
+class JS_PUBLIC_API JSTracer {
+ public:
+ // Return the runtime set on the tracer.
+ JSRuntime* runtime() const { return runtime_; }
+
+ JS::TracerKind kind() const { return kind_; }
+ bool isGenericTracer() const { return kind_ < JS::TracerKind::Callback; }
+ bool isCallbackTracer() const { return kind_ >= JS::TracerKind::Callback; }
+ bool isMarkingTracer() const { return kind_ == JS::TracerKind::Marking; }
+ bool isTenuringTracer() const { return kind_ == JS::TracerKind::Tenuring; }
+
+ inline JS::CallbackTracer* asCallbackTracer();
+
+ JS::WeakMapTraceAction weakMapAction() const {
+ return options_.weakMapAction;
+ }
+ bool traceWeakEdges() const {
+ return options_.weakEdgeAction == JS::WeakEdgeTraceAction::Trace;
+ }
+
+ JS::TracingContext& context() { return context_; }
+
+ // These methods are called when the tracer encounters an edge. Clients should
+ // override them to receive notifications when an edge of each type is
+ // visited.
+ //
+ // The caller updates the edge with the return value (if different).
+ //
+ // 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 one type
+ // of edge.
+#define DEFINE_ON_EDGE_METHOD(name, type, _1, _2) \
+ virtual void on##name##Edge(type** thingp, const char* name) = 0;
+ JS_FOR_EACH_TRACEKIND(DEFINE_ON_EDGE_METHOD)
+#undef DEFINE_ON_EDGE_METHOD
+
+ protected:
+ JSTracer(JSRuntime* rt, JS::TracerKind kind,
+ JS::TraceOptions options = JS::TraceOptions())
+ : runtime_(rt), kind_(kind), options_(options) {}
+
+ private:
+ JSRuntime* const runtime_;
+ const JS::TracerKind kind_;
+ const JS::TraceOptions options_;
+ JS::TracingContext context_;
+};
+
+namespace js {
+
+// A CRTP helper class that implements a JSTracer by calling a template method
+// on the derived tracer type for each edge kind.
+template <typename T>
+class GenericTracerImpl : public JSTracer {
+ public:
+ GenericTracerImpl(JSRuntime* rt, JS::TracerKind kind,
+ JS::TraceOptions options)
+ : JSTracer(rt, kind, options) {}
+
+ private:
+ T* derived() { return static_cast<T*>(this); }
+
+#define DEFINE_ON_EDGE_METHOD(name, type, _1, _2) \
+ void on##name##Edge(type** thingp, const char* name) final { \
+ derived()->onEdge(thingp, name); \
+ }
+ JS_FOR_EACH_TRACEKIND(DEFINE_ON_EDGE_METHOD)
+#undef DEFINE_ON_EDGE_METHOD
+};
+
+} // namespace js
+
+namespace JS {
+
+class JS_PUBLIC_API CallbackTracer
+ : public js::GenericTracerImpl<CallbackTracer> {
+ public:
+ CallbackTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Callback,
+ JS::TraceOptions options = JS::TraceOptions())
+ : GenericTracerImpl(rt, kind, options) {
+ MOZ_ASSERT(isCallbackTracer());
+ }
+ CallbackTracer(JSContext* cx, JS::TracerKind kind = JS::TracerKind::Callback,
+ JS::TraceOptions options = JS::TraceOptions());
+
+ // Override this method to receive notification when a node in the GC
+ // heap graph is visited.
+ virtual void onChild(JS::GCCellPtr thing, const char* name) = 0;
+
+ private:
+ template <typename T>
+ void onEdge(T** thingp, const char* name) {
+ onChild(JS::GCCellPtr(*thingp), name);
+ }
+ friend class js::GenericTracerImpl<CallbackTracer>;
+};
+
+// Set the index portion of the tracer's context for the current range.
+class MOZ_RAII AutoTracingIndex {
+ JSTracer* trc_;
+
+ public:
+ explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(trc) {
+ MOZ_ASSERT(trc_->context().index_ == TracingContext::InvalidIndex);
+ trc_->context().index_ = initial;
+ }
+ ~AutoTracingIndex() {
+ MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex);
+ trc_->context().index_ = TracingContext::InvalidIndex;
+ }
+
+ void operator++() {
+ MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex);
+ ++trc_->context().index_;
+ }
+};
+
+// Set a context callback for the trace callback to use, if it needs a detailed
+// edge description.
+class MOZ_RAII AutoTracingDetails {
+ JSTracer* trc_;
+
+ public:
+ AutoTracingDetails(JSTracer* trc, TracingContext::Functor& func) : trc_(trc) {
+ MOZ_ASSERT(trc_->context().functor_ == nullptr);
+ trc_->context().functor_ = &func;
+ }
+ ~AutoTracingDetails() {
+ MOZ_ASSERT(trc_->context().functor_);
+ trc_->context().functor_ = nullptr;
+ }
+};
+
+// Save and clear tracing context when performing nested tracing.
+class MOZ_RAII AutoClearTracingContext {
+ JSTracer* trc_;
+ TracingContext prev_;
+
+ public:
+ explicit AutoClearTracingContext(JSTracer* trc)
+ : trc_(trc), prev_(trc->context()) {
+ trc_->context() = TracingContext();
+ }
+
+ ~AutoClearTracingContext() { trc_->context() = prev_; }
+};
+
+} // namespace JS
+
+JS::CallbackTracer* JSTracer::asCallbackTracer() {
+ MOZ_ASSERT(isCallbackTracer());
+ return static_cast<JS::CallbackTracer*>(this);
+}
+
+namespace js {
+
+class AbstractGeneratorObject;
+class SavedFrame;
+
+namespace gc {
+
+#define JS_DECLARE_TRACE_EXTERNAL_EDGE(type) \
+ extern JS_PUBLIC_API void TraceExternalEdge(JSTracer* trc, type* thingp, \
+ const char* name);
+
+// Declare edge-tracing function overloads for public GC pointer types.
+JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE)
+JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE)
+
+#undef JS_DECLARE_TRACE_EXTERNAL_EDGE
+
+} // namespace gc
+} // namespace js
+
+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>
+inline void TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
+ MOZ_ASSERT(thingp);
+ if (*thingp) {
+ js::gc::TraceExternalEdge(trc, thingp->unsafeGet(), name);
+ }
+}
+
+template <typename T>
+inline void TraceEdge(JSTracer* trc, JS::TenuredHeap<T>* thingp,
+ const char* name) {
+ MOZ_ASSERT(thingp);
+ if (T ptr = thingp->unbarrieredGetPtr()) {
+ js::gc::TraceExternalEdge(trc, &ptr, name);
+ thingp->setPtr(ptr);
+ }
+}
+
+// Edges that are always traced as part of root marking do not require
+// incremental barriers. |JS::TraceRoot| overloads allow for marking
+// non-barriered pointers but assert that this happens during root marking.
+//
+// Note that while |edgep| must never be null, it is fine for |*edgep| to be
+// nullptr.
+#define JS_DECLARE_TRACE_ROOT(type) \
+ extern JS_PUBLIC_API void TraceRoot(JSTracer* trc, type* edgep, \
+ const char* name);
+
+// Declare edge-tracing function overloads for public GC pointer types.
+JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_TRACE_ROOT)
+JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_TRACE_ROOT)
+
+// We also require overloads for these purely-internal types. These overloads
+// ought not be in public headers, and they should use a different name in order
+// to not be *actual* overloads, but for the moment we still declare them here.
+JS_DECLARE_TRACE_ROOT(js::AbstractGeneratorObject*)
+JS_DECLARE_TRACE_ROOT(js::SavedFrame*)
+
+#undef JS_DECLARE_TRACE_ROOT
+
+extern JS_PUBLIC_API void TraceChildren(JSTracer* trc, GCCellPtr thing);
+
+} // namespace JS
+
+namespace js {
+
+inline bool IsTracerKind(JSTracer* trc, JS::TracerKind kind) {
+ return trc->kind() == kind;
+}
+
+// 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.
+extern JS_PUBLIC_API void UnsafeTraceManuallyBarrieredEdge(JSTracer* trc,
+ JSObject** 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 TraceWeakEdge(JSTracer* trc, JS::Heap<T>* thingp);
+
+} // namespace gc
+
+#ifdef DEBUG
+/*
+ * Return whether the runtime is currently being destroyed, for use in
+ * assertions.
+ */
+extern JS_PUBLIC_API bool RuntimeIsBeingDestroyed();
+#endif
+
+} // namespace js
+
+#endif /* js_TracingAPI_h */