1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #ifndef js_TracingAPI_h
8 #define js_TracingAPI_h
10 #include "js/GCTypeMacros.h"
11 #include "js/HeapAPI.h"
12 #include "js/TraceKind.h"
14 class JS_PUBLIC_API JSTracer
;
17 class JS_PUBLIC_API CallbackTracer
;
23 /** Returns a static string equivalent of |kind|. */
24 JS_FRIEND_API
const char* GCTraceKindToAscii(JS::TraceKind kind
);
26 /** Returns the base size in bytes of the GC thing of kind |kind|. */
27 JS_FRIEND_API
size_t GCTraceKindSize(JS::TraceKind kind
);
30 enum class TracerKind
{
31 // Marking path: a tracer used only for marking liveness of cells, not
35 // Generic tracers: Internal tracers that have a different virtual method
36 // called for each edge kind.
38 // Order is important. All generic kinds must follow this one.
41 // Specific kinds of generic tracer.
48 // Callback tracers: General-purpose tracers that have a single virtual
49 // method called on every edge.
51 // Order is important. All callback kinds must follow this one.
54 // Specific kinds of callback tracer.
56 VerifyTraceProtoAndIface
59 enum class WeakMapTraceAction
{
61 * Do not trace into weak map keys or values during traversal. Users must
62 * handle weak maps manually.
67 * Do true ephemeron marking with a weak key lookup marking phase. This is
68 * the default for GCMarker.
73 * Trace through to all values, irrespective of whether the keys are live
74 * or not. Used for non-marking tracers.
79 * Trace through to all keys and values, irrespective of whether the keys
80 * are live or not. Used for non-marking tracers.
85 // Whether a tracer should trace weak edges. GCMarker sets this to Skip.
86 enum class WeakEdgeTraceAction
{ Skip
, Trace
};
88 // Whether a tracer can skip tracing JS::Ids. This is needed by the cycle
89 // collector to skip some Ids for performance reasons. Not all Ids are skipped.
90 enum class IdTraceAction
{ CanSkip
, Trace
};
93 JS::WeakMapTraceAction weakMapAction
= WeakMapTraceAction::TraceValues
;
94 JS::WeakEdgeTraceAction weakEdgeAction
= WeakEdgeTraceAction::Trace
;
95 JS::IdTraceAction idAction
= IdTraceAction::Trace
;
97 TraceOptions() = default;
98 TraceOptions(JS::WeakMapTraceAction weakMapActionArg
,
99 JS::WeakEdgeTraceAction weakEdgeActionArg
,
100 JS::IdTraceAction idActionArg
= IdTraceAction::Trace
)
101 : weakMapAction(weakMapActionArg
),
102 weakEdgeAction(weakEdgeActionArg
),
103 idAction(idActionArg
) {}
104 MOZ_IMPLICIT
TraceOptions(JS::WeakMapTraceAction weakMapActionArg
)
105 : weakMapAction(weakMapActionArg
) {}
106 MOZ_IMPLICIT
TraceOptions(JS::WeakEdgeTraceAction weakEdgeActionArg
)
107 : weakEdgeAction(weakEdgeActionArg
) {}
108 MOZ_IMPLICIT
TraceOptions(JS::IdTraceAction idActionArg
)
109 : idAction(idActionArg
) {}
112 class AutoTracingName
;
113 class AutoTracingIndex
;
115 // Optional context information that can be used to construct human readable
116 // descriptions of what is being traced.
117 class TracingContext
{
119 // Access to the tracing context: When tracing with a JS::CallbackTracer, we
120 // invoke the callback with the edge location and the type of target. This is
121 // useful for operating on the edge in the abstract or on the target thing,
122 // satisfying most common use cases. However, some tracers need additional
123 // detail about the specific edge that is being traced in order to be
124 // useful. Unfortunately, the raw pointer to the edge that we provide is not
125 // enough information to infer much of anything useful about that edge.
127 // In order to better support use cases that care in particular about edges --
128 // as opposed to the target thing -- tracing implementations are responsible
129 // for providing extra context information about each edge they trace, as it
130 // is traced. This contains, at a minimum, an edge name and, when tracing an
131 // array, the index. Further specialization can be achieved (with some
132 // complexity), by associating a functor with the tracer so that, when
133 // requested, the user can generate totally custom edge descriptions.
135 // Returns the current edge's name. It is only valid to call this when
136 // inside the trace callback, however, the edge name will always be set.
137 const char* name() const {
142 // Returns the current edge's index, if marked as part of an array of edges.
143 // This must be called only inside the trace callback. When not tracing an
144 // array, the value will be InvalidIndex.
145 constexpr static size_t InvalidIndex
= size_t(-1);
146 size_t index() const { return index_
; }
148 // Build a description of this edge in the heap graph. This call may invoke
149 // the context functor, if set, which may inspect arbitrary areas of the
150 // heap. On the other hand, the description provided by this method may be
151 // substantially more accurate and useful than those provided by only the
153 void getEdgeName(char* buffer
, size_t bufferSize
);
155 // The trace implementation may associate a callback with one or more edges
156 // using AutoTracingDetails. This functor is called by getEdgeName and
157 // is responsible for providing a textual representation of the edge currently
158 // being traced. The callback has access to the full heap, including the
159 // currently set tracing context.
162 virtual void operator()(TracingContext
* tcx
, char* buf
, size_t bufsize
) = 0;
166 friend class AutoTracingName
;
167 const char* name_
= nullptr;
169 friend class AutoTracingIndex
;
170 size_t index_
= InvalidIndex
;
172 friend class AutoTracingDetails
;
173 Functor
* functor_
= nullptr;
182 class JS_PUBLIC_API JSTracer
{
184 // Return the runtime set on the tracer.
185 JSRuntime
* runtime() const { return runtime_
; }
187 JS::TracerKind
kind() const { return kind_
; }
188 bool isMarkingTracer() const { return kind_
== JS::TracerKind::Marking
; }
189 bool isTenuringTracer() const { return kind_
== JS::TracerKind::Tenuring
; }
190 bool isGenericTracer() const { return kind_
>= JS::TracerKind::Generic
; }
191 bool isCallbackTracer() const { return kind_
>= JS::TracerKind::Callback
; }
193 inline js::GenericTracer
* asGenericTracer();
194 inline JS::CallbackTracer
* asCallbackTracer();
196 JS::WeakMapTraceAction
weakMapAction() const {
197 return options_
.weakMapAction
;
199 bool traceWeakEdges() const {
200 return options_
.weakEdgeAction
== JS::WeakEdgeTraceAction::Trace
;
202 bool canSkipJsids() const {
203 return options_
.idAction
== JS::IdTraceAction::CanSkip
;
206 JS::TracingContext
& context() { return context_
; }
208 // Get the current GC number. Only call this method if |isMarkingTracer()|
210 uint32_t gcNumberForMarking() const;
213 JSTracer(JSRuntime
* rt
, JS::TracerKind kind
,
214 JS::TraceOptions options
= JS::TraceOptions())
215 : runtime_(rt
), kind_(kind
), options_(options
) {}
218 JSRuntime
* const runtime_
;
219 const JS::TracerKind kind_
;
220 const JS::TraceOptions options_
;
221 JS::TracingContext context_
;
226 class GenericTracer
: public JSTracer
{
228 GenericTracer(JSRuntime
* rt
, JS::TracerKind kind
= JS::TracerKind::Generic
,
229 JS::TraceOptions options
= JS::TraceOptions())
230 : JSTracer(rt
, kind
, options
) {
231 MOZ_ASSERT(isGenericTracer());
234 // These methods are called when the tracer encounters an edge. Clients should
235 // override them to receive notifications when an edge of each type is
238 // The caller updates the edge with the return value (if different).
240 // In C++, overriding a method hides all methods in the base class with that
241 // name, not just methods with that signature. Thus, the typed edge methods
242 // have to have distinct names to allow us to override them individually,
243 // which is freqently useful if, for example, we only want to process one type
245 virtual JSObject
* onObjectEdge(JSObject
* obj
) = 0;
246 virtual JSString
* onStringEdge(JSString
* str
) = 0;
247 virtual JS::Symbol
* onSymbolEdge(JS::Symbol
* sym
) = 0;
248 virtual JS::BigInt
* onBigIntEdge(JS::BigInt
* bi
) = 0;
249 virtual js::BaseScript
* onScriptEdge(js::BaseScript
* script
) = 0;
250 virtual js::Shape
* onShapeEdge(js::Shape
* shape
) = 0;
251 virtual js::RegExpShared
* onRegExpSharedEdge(js::RegExpShared
* shared
) = 0;
252 virtual js::ObjectGroup
* onObjectGroupEdge(js::ObjectGroup
* group
) = 0;
253 virtual js::BaseShape
* onBaseShapeEdge(js::BaseShape
* base
) = 0;
254 virtual js::jit::JitCode
* onJitCodeEdge(js::jit::JitCode
* code
) = 0;
255 virtual js::Scope
* onScopeEdge(js::Scope
* scope
) = 0;
262 class JS_PUBLIC_API CallbackTracer
: public js::GenericTracer
{
264 CallbackTracer(JSRuntime
* rt
, JS::TracerKind kind
= JS::TracerKind::Callback
,
265 JS::TraceOptions options
= JS::TraceOptions())
266 : GenericTracer(rt
, kind
, options
) {
267 MOZ_ASSERT(isCallbackTracer());
269 CallbackTracer(JSContext
* cx
, JS::TracerKind kind
= JS::TracerKind::Callback
,
270 JS::TraceOptions options
= JS::TraceOptions());
272 // Override this method to receive notification when a node in the GC
273 // heap graph is visited.
274 virtual void onChild(const JS::GCCellPtr
& thing
) = 0;
277 // This class implements the GenericTracer interface to dispatches to onChild.
278 virtual JSObject
* onObjectEdge(JSObject
* obj
) {
279 onChild(JS::GCCellPtr(obj
));
282 virtual JSString
* onStringEdge(JSString
* str
) {
283 onChild(JS::GCCellPtr(str
));
286 virtual JS::Symbol
* onSymbolEdge(JS::Symbol
* sym
) {
287 onChild(JS::GCCellPtr(sym
));
290 virtual JS::BigInt
* onBigIntEdge(JS::BigInt
* bi
) {
291 onChild(JS::GCCellPtr(bi
));
294 virtual js::BaseScript
* onScriptEdge(js::BaseScript
* script
) {
295 onChild(JS::GCCellPtr(script
));
298 virtual js::Shape
* onShapeEdge(js::Shape
* shape
) {
299 onChild(JS::GCCellPtr(shape
, JS::TraceKind::Shape
));
302 virtual js::ObjectGroup
* onObjectGroupEdge(js::ObjectGroup
* group
) {
303 onChild(JS::GCCellPtr(group
, JS::TraceKind::ObjectGroup
));
306 virtual js::BaseShape
* onBaseShapeEdge(js::BaseShape
* base
) {
307 onChild(JS::GCCellPtr(base
, JS::TraceKind::BaseShape
));
310 virtual js::jit::JitCode
* onJitCodeEdge(js::jit::JitCode
* code
) {
311 onChild(JS::GCCellPtr(code
, JS::TraceKind::JitCode
));
314 virtual js::Scope
* onScopeEdge(js::Scope
* scope
) {
315 onChild(JS::GCCellPtr(scope
, JS::TraceKind::Scope
));
318 virtual js::RegExpShared
* onRegExpSharedEdge(js::RegExpShared
* shared
) {
319 onChild(JS::GCCellPtr(shared
, JS::TraceKind::RegExpShared
));
324 // Set the name portion of the tracer's context for the current edge.
325 class MOZ_RAII AutoTracingName
{
329 AutoTracingName(JSTracer
* trc
, const char* name
) : trc_(trc
) {
331 MOZ_ASSERT(!trc_
->context().name_
);
332 trc_
->context().name_
= name
;
335 MOZ_ASSERT(trc_
->context().name_
);
336 trc_
->context().name_
= nullptr;
340 // Set the index portion of the tracer's context for the current range.
341 class MOZ_RAII AutoTracingIndex
{
345 explicit AutoTracingIndex(JSTracer
* trc
, size_t initial
= 0) : trc_(trc
) {
346 MOZ_ASSERT(trc_
->context().index_
== TracingContext::InvalidIndex
);
347 trc_
->context().index_
= initial
;
349 ~AutoTracingIndex() {
350 MOZ_ASSERT(trc_
->context().index_
!= TracingContext::InvalidIndex
);
351 trc_
->context().index_
= TracingContext::InvalidIndex
;
355 MOZ_ASSERT(trc_
->context().index_
!= TracingContext::InvalidIndex
);
356 ++trc_
->context().index_
;
360 // Set a context callback for the trace callback to use, if it needs a detailed
362 class MOZ_RAII AutoTracingDetails
{
366 AutoTracingDetails(JSTracer
* trc
, TracingContext::Functor
& func
) : trc_(trc
) {
367 MOZ_ASSERT(trc_
->context().functor_
== nullptr);
368 trc_
->context().functor_
= &func
;
370 ~AutoTracingDetails() {
371 MOZ_ASSERT(trc_
->context().functor_
);
372 trc_
->context().functor_
= nullptr;
376 // Save and clear tracing context when performing nested tracing.
377 class MOZ_RAII AutoClearTracingContext
{
379 TracingContext prev_
;
382 explicit AutoClearTracingContext(JSTracer
* trc
)
383 : trc_(trc
), prev_(trc
->context()) {
384 trc_
->context() = TracingContext();
387 ~AutoClearTracingContext() { trc_
->context() = prev_
; }
392 js::GenericTracer
* JSTracer::asGenericTracer() {
393 MOZ_ASSERT(isGenericTracer());
394 return static_cast<js::GenericTracer
*>(this);
397 JS::CallbackTracer
* JSTracer::asCallbackTracer() {
398 MOZ_ASSERT(isCallbackTracer());
399 return static_cast<JS::CallbackTracer
*>(this);
404 class AbstractGeneratorObject
;
409 #define JS_DECLARE_TRACE_EXTERNAL_EDGE(type) \
410 extern JS_PUBLIC_API void TraceExternalEdge(JSTracer* trc, type* thingp, \
413 // Declare edge-tracing function overloads for public GC pointer types.
414 JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE
)
415 JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE
)
417 #undef JS_DECLARE_TRACE_EXTERNAL_EDGE
424 // The JS::TraceEdge family of functions traces the given GC thing reference.
425 // This performs the tracing action configured on the given JSTracer: typically
426 // calling the JSTracer::callback or marking the thing as live.
428 // The argument to JS::TraceEdge is an in-out param: when the function returns,
429 // the garbage collector might have moved the GC thing. In this case, the
430 // reference passed to JS::TraceEdge will be updated to the thing's new
431 // location. Callers of this method are responsible for updating any state that
432 // is dependent on the object's address. For example, if the object's address
433 // is used as a key in a hashtable, then the object must be removed and
434 // re-inserted with the correct hash.
436 // Note that while |edgep| must never be null, it is fine for |*edgep| to be
439 template <typename T
>
440 inline void TraceEdge(JSTracer
* trc
, JS::Heap
<T
>* thingp
, const char* name
) {
443 js::gc::TraceExternalEdge(trc
, thingp
->unsafeGet(), name
);
447 template <typename T
>
448 inline void TraceEdge(JSTracer
* trc
, JS::TenuredHeap
<T
>* thingp
,
451 if (T ptr
= thingp
->unbarrieredGetPtr()) {
452 js::gc::TraceExternalEdge(trc
, &ptr
, name
);
457 // Edges that are always traced as part of root marking do not require
458 // incremental barriers. |JS::UnsafeTraceRoot| overloads allow for marking
459 // non-barriered pointers but assert that this happens during root marking.
461 // Note that while |edgep| must never be null, it is fine for |*edgep| to be
463 #define JS_DECLARE_UNSAFE_TRACE_ROOT(type) \
464 extern JS_PUBLIC_API void UnsafeTraceRoot(JSTracer* trc, type* edgep, \
467 // Declare edge-tracing function overloads for public GC pointer types.
468 JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_UNSAFE_TRACE_ROOT
)
469 JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_UNSAFE_TRACE_ROOT
)
471 // We also require overloads for these purely-internal types. These overloads
472 // ought not be in public headers, and they should use a different name in order
473 // to not be *actual* overloads, but for the moment we still declare them here.
474 JS_DECLARE_UNSAFE_TRACE_ROOT(js::AbstractGeneratorObject
*)
475 JS_DECLARE_UNSAFE_TRACE_ROOT(js::SavedFrame
*)
477 #undef JS_DECLARE_UNSAFE_TRACE_ROOT
479 extern JS_PUBLIC_API
void TraceChildren(JSTracer
* trc
, GCCellPtr thing
);
485 inline bool IsTracerKind(JSTracer
* trc
, JS::TracerKind kind
) {
486 return trc
->kind() == kind
;
489 // Trace an edge that is not a GC root and is not wrapped in a barriered
490 // wrapper for some reason.
492 // This method does not check if |*edgep| is non-null before tracing through
493 // it, so callers must check any nullable pointer before calling this method.
494 extern JS_PUBLIC_API
void UnsafeTraceManuallyBarrieredEdge(JSTracer
* trc
,
498 // Not part of the public API, but declared here so we can use it in
500 template <typename T
>
501 inline bool TraceManuallyBarrieredWeakEdge(JSTracer
* trc
, T
* thingp
,
504 template <typename T
>
507 template <typename T
>
508 inline bool TraceWeakEdge(JSTracer
* trc
, BarrieredBase
<T
>* thingp
,
513 // Return true if the given edge is not live and is about to be swept.
514 template <typename T
>
515 extern JS_PUBLIC_API
bool EdgeNeedsSweep(JS::Heap
<T
>* edgep
);
517 // Not part of the public API, but declared here so we can use it in GCPolicy
519 template <typename T
>
520 bool IsAboutToBeFinalizedUnbarriered(T
* thingp
);
526 * Return whether the runtime is currently being destroyed, for use in
529 extern JS_FRIEND_API
bool RuntimeIsBeingDestroyed();
534 #endif /* js_TracingAPI_h */