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 : #ifndef js_TracingAPI_h
8 : #define js_TracingAPI_h
9 :
10 : #include "jsalloc.h"
11 :
12 : #include "js/HashTable.h"
13 : #include "js/HeapAPI.h"
14 : #include "js/TraceKind.h"
15 :
16 : class JS_PUBLIC_API(JSTracer);
17 :
18 : namespace JS {
19 : class JS_PUBLIC_API(CallbackTracer);
20 : template <typename T> class Heap;
21 : template <typename T> class TenuredHeap;
22 :
23 : /** Returns a static string equivalent of |kind|. */
24 : JS_FRIEND_API(const char*)
25 : GCTraceKindToAscii(JS::TraceKind kind);
26 :
27 : } // namespace JS
28 :
29 : enum WeakMapTraceKind {
30 : /**
31 : * Do not trace into weak map keys or values during traversal. Users must
32 : * handle weak maps manually.
33 : */
34 : DoNotTraceWeakMaps,
35 :
36 : /**
37 : * Do true ephemeron marking with a weak key lookup marking phase. This is
38 : * the default for GCMarker.
39 : */
40 : ExpandWeakMaps,
41 :
42 : /**
43 : * Trace through to all values, irrespective of whether the keys are live
44 : * or not. Used for non-marking tracers.
45 : */
46 : TraceWeakMapValues,
47 :
48 : /**
49 : * Trace through to all keys and values, irrespective of whether the keys
50 : * are live or not. Used for non-marking tracers.
51 : */
52 : TraceWeakMapKeysValues
53 : };
54 :
55 : class JS_PUBLIC_API(JSTracer)
56 : {
57 : public:
58 : // Return the runtime set on the tracer.
59 201400 : JSRuntime* runtime() const { return runtime_; }
60 :
61 : // Return the weak map tracing behavior currently set on this tracer.
62 28 : WeakMapTraceKind weakMapAction() const { return weakMapAction_; }
63 :
64 : enum class TracerKindTag {
65 : // Marking path: a tracer used only for marking liveness of cells, not
66 : // for moving them. The kind will transition to WeakMarking after
67 : // everything reachable by regular edges has been marked.
68 : Marking,
69 :
70 : // Same as Marking, except we have now moved on to the "weak marking
71 : // phase", in which every marked obj/script is immediately looked up to
72 : // see if it is a weak map key (and therefore might require marking its
73 : // weak map value).
74 : WeakMarking,
75 :
76 : // A tracer that traverses the graph for the purposes of moving objects
77 : // from the nursery to the tenured area.
78 : Tenuring,
79 :
80 : // General-purpose traversal that invokes a callback on each cell.
81 : // Traversing children is the responsibility of the callback.
82 : Callback
83 : };
84 248646 : bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
85 4261 : bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
86 108120 : bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
87 233606 : bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
88 : inline JS::CallbackTracer* asCallbackTracer();
89 0 : bool traceWeakEdges() const { return traceWeakEdges_; }
90 : #ifdef DEBUG
91 46740 : bool checkEdges() { return checkEdges_; }
92 : #endif
93 :
94 : protected:
95 51 : JSTracer(JSRuntime* rt, TracerKindTag tag,
96 : WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
97 51 : : runtime_(rt)
98 : , weakMapAction_(weakTraceKind)
99 : #ifdef DEBUG
100 : , checkEdges_(true)
101 : #endif
102 : , tag_(tag)
103 51 : , traceWeakEdges_(true)
104 51 : {}
105 :
106 : #ifdef DEBUG
107 : // Set whether to check edges are valid in debug builds.
108 0 : void setCheckEdges(bool check) {
109 0 : checkEdges_ = check;
110 0 : }
111 : #endif
112 :
113 : private:
114 : JSRuntime* runtime_;
115 : WeakMapTraceKind weakMapAction_;
116 : #ifdef DEBUG
117 : bool checkEdges_;
118 : #endif
119 :
120 : protected:
121 : TracerKindTag tag_;
122 : bool traceWeakEdges_;
123 : };
124 :
125 : namespace JS {
126 :
127 : class AutoTracingName;
128 : class AutoTracingIndex;
129 : class AutoTracingCallback;
130 :
131 : class JS_PUBLIC_API(CallbackTracer) : public JSTracer
132 : {
133 : public:
134 26 : CallbackTracer(JSRuntime* rt, WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
135 26 : : JSTracer(rt, JSTracer::TracerKindTag::Callback, weakTraceKind),
136 26 : contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr)
137 26 : {}
138 : CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind = TraceWeakMapValues);
139 :
140 : // Override these methods to receive notification when an edge is visited
141 : // with the type contained in the callback. The default implementation
142 : // dispatches to the fully-generic onChild implementation, so for cases that
143 : // do not care about boxing overhead and do not need the actual edges,
144 : // just override the generic onChild.
145 25 : virtual void onObjectEdge(JSObject** objp) { onChild(JS::GCCellPtr(*objp)); }
146 0 : virtual void onStringEdge(JSString** strp) { onChild(JS::GCCellPtr(*strp)); }
147 0 : virtual void onSymbolEdge(JS::Symbol** symp) { onChild(JS::GCCellPtr(*symp)); }
148 0 : virtual void onScriptEdge(JSScript** scriptp) { onChild(JS::GCCellPtr(*scriptp)); }
149 25 : virtual void onShapeEdge(js::Shape** shapep) {
150 25 : onChild(JS::GCCellPtr(*shapep, JS::TraceKind::Shape));
151 25 : }
152 25 : virtual void onObjectGroupEdge(js::ObjectGroup** groupp) {
153 25 : onChild(JS::GCCellPtr(*groupp, JS::TraceKind::ObjectGroup));
154 25 : }
155 0 : virtual void onBaseShapeEdge(js::BaseShape** basep) {
156 0 : onChild(JS::GCCellPtr(*basep, JS::TraceKind::BaseShape));
157 0 : }
158 0 : virtual void onJitCodeEdge(js::jit::JitCode** codep) {
159 0 : onChild(JS::GCCellPtr(*codep, JS::TraceKind::JitCode));
160 0 : }
161 0 : virtual void onLazyScriptEdge(js::LazyScript** lazyp) {
162 0 : onChild(JS::GCCellPtr(*lazyp, JS::TraceKind::LazyScript));
163 0 : }
164 25 : virtual void onScopeEdge(js::Scope** scopep) {
165 25 : onChild(JS::GCCellPtr(*scopep, JS::TraceKind::Scope));
166 25 : }
167 0 : virtual void onRegExpSharedEdge(js::RegExpShared** sharedp) {
168 0 : onChild(JS::GCCellPtr(*sharedp, JS::TraceKind::RegExpShared));
169 0 : }
170 :
171 : // Override this method to receive notification when a node in the GC
172 : // heap graph is visited.
173 : virtual void onChild(const JS::GCCellPtr& thing) = 0;
174 :
175 : // Access to the tracing context:
176 : // When tracing with a JS::CallbackTracer, we invoke the callback with the
177 : // edge location and the type of target. This is useful for operating on
178 : // the edge in the abstract or on the target thing, satisfying most common
179 : // use cases. However, some tracers need additional detail about the
180 : // specific edge that is being traced in order to be useful. Unfortunately,
181 : // the raw pointer to the edge that we provide is not enough information to
182 : // infer much of anything useful about that edge.
183 : //
184 : // In order to better support use cases that care in particular about edges
185 : // -- as opposed to the target thing -- tracing implementations are
186 : // responsible for providing extra context information about each edge they
187 : // trace, as it is traced. This contains, at a minimum, an edge name and,
188 : // when tracing an array, the index. Further specialization can be achived
189 : // (with some complexity), by associating a functor with the tracer so
190 : // that, when requested, the user can generate totally custom edge
191 : // descriptions.
192 :
193 : // Returns the current edge's name. It is only valid to call this when
194 : // inside the trace callback, however, the edge name will always be set.
195 0 : const char* contextName() const { MOZ_ASSERT(contextName_); return contextName_; }
196 :
197 : // Returns the current edge's index, if marked as part of an array of edges.
198 : // This must be called only inside the trace callback. When not tracing an
199 : // array, the value will be InvalidIndex.
200 : const static size_t InvalidIndex = size_t(-1);
201 0 : size_t contextIndex() const { return contextIndex_; }
202 :
203 : // Build a description of this edge in the heap graph. This call may invoke
204 : // the context functor, if set, which may inspect arbitrary areas of the
205 : // heap. On the other hand, the description provided by this method may be
206 : // substantially more accurate and useful than those provided by only the
207 : // contextName and contextIndex.
208 : void getTracingEdgeName(char* buffer, size_t bufferSize);
209 :
210 : // The trace implementation may associate a callback with one or more edges
211 : // using AutoTracingDetails. This functor is called by getTracingEdgeName
212 : // and is responsible for providing a textual representation of the
213 : // currently being traced edge. The callback has access to the full heap,
214 : // including the currently set tracing context.
215 25 : class ContextFunctor {
216 : public:
217 : virtual void operator()(CallbackTracer* trc, char* buf, size_t bufsize) = 0;
218 : };
219 :
220 : #ifdef DEBUG
221 : enum class TracerKind {
222 : DoNotCare,
223 : Moving,
224 : GrayBuffering,
225 : VerifyTraceProtoAndIface,
226 : ClearEdges
227 : };
228 0 : virtual TracerKind getTracerKind() const { return TracerKind::DoNotCare; }
229 : #endif
230 :
231 : // In C++, overriding a method hides all methods in the base class with
232 : // that name, not just methods with that signature. Thus, the typed edge
233 : // methods have to have distinct names to allow us to override them
234 : // individually, which is freqently useful if, for example, we only want to
235 : // process only one type of edge.
236 5084 : void dispatchToOnEdge(JSObject** objp) { onObjectEdge(objp); }
237 4 : void dispatchToOnEdge(JSString** strp) { onStringEdge(strp); }
238 0 : void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); }
239 39 : void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
240 25 : void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
241 25 : void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
242 0 : void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
243 0 : void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
244 0 : void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
245 25 : void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
246 0 : void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); }
247 :
248 : protected:
249 0 : void setTraceWeakEdges(bool value) {
250 0 : traceWeakEdges_ = value;
251 0 : }
252 :
253 : private:
254 : friend class AutoTracingName;
255 : const char* contextName_;
256 :
257 : friend class AutoTracingIndex;
258 : size_t contextIndex_;
259 :
260 : friend class AutoTracingDetails;
261 : ContextFunctor* contextFunctor_;
262 : };
263 :
264 : // Set the name portion of the tracer's context for the current edge.
265 : class MOZ_RAII AutoTracingName
266 : {
267 : CallbackTracer* trc_;
268 : const char* prior_;
269 :
270 : public:
271 5202 : AutoTracingName(CallbackTracer* trc, const char* name) : trc_(trc), prior_(trc->contextName_) {
272 5202 : MOZ_ASSERT(name);
273 5202 : trc->contextName_ = name;
274 5202 : }
275 10404 : ~AutoTracingName() {
276 5202 : MOZ_ASSERT(trc_->contextName_);
277 5202 : trc_->contextName_ = prior_;
278 5202 : }
279 : };
280 :
281 : // Set the index portion of the tracer's context for the current range.
282 : class MOZ_RAII AutoTracingIndex
283 : {
284 : CallbackTracer* trc_;
285 :
286 : public:
287 7583 : explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(nullptr) {
288 7583 : if (trc->isCallbackTracer()) {
289 50 : trc_ = trc->asCallbackTracer();
290 50 : MOZ_ASSERT(trc_->contextIndex_ == CallbackTracer::InvalidIndex);
291 50 : trc_->contextIndex_ = initial;
292 : }
293 7583 : }
294 15166 : ~AutoTracingIndex() {
295 7583 : if (trc_) {
296 50 : MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
297 50 : trc_->contextIndex_ = CallbackTracer::InvalidIndex;
298 : }
299 7583 : }
300 :
301 19408 : void operator++() {
302 19408 : if (trc_) {
303 3900 : MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
304 3900 : ++trc_->contextIndex_;
305 : }
306 19408 : }
307 : };
308 :
309 : // Set a context callback for the trace callback to use, if it needs a detailed
310 : // edge description.
311 : class MOZ_RAII AutoTracingDetails
312 : {
313 : CallbackTracer* trc_;
314 :
315 : public:
316 25 : AutoTracingDetails(JSTracer* trc, CallbackTracer::ContextFunctor& func) : trc_(nullptr) {
317 25 : if (trc->isCallbackTracer()) {
318 25 : trc_ = trc->asCallbackTracer();
319 25 : MOZ_ASSERT(trc_->contextFunctor_ == nullptr);
320 25 : trc_->contextFunctor_ = &func;
321 : }
322 25 : }
323 50 : ~AutoTracingDetails() {
324 25 : if (trc_) {
325 25 : MOZ_ASSERT(trc_->contextFunctor_);
326 25 : trc_->contextFunctor_ = nullptr;
327 : }
328 25 : }
329 : };
330 :
331 : } // namespace JS
332 :
333 : JS::CallbackTracer*
334 29960 : JSTracer::asCallbackTracer()
335 : {
336 29960 : MOZ_ASSERT(isCallbackTracer());
337 29960 : return static_cast<JS::CallbackTracer*>(this);
338 : }
339 :
340 : namespace js {
341 : namespace gc {
342 : template <typename T>
343 : JS_PUBLIC_API(void) TraceExternalEdge(JSTracer* trc, T* thingp, const char* name);
344 : } // namespace gc
345 : } // namespace js
346 :
347 : namespace JS {
348 :
349 : // The JS::TraceEdge family of functions traces the given GC thing reference.
350 : // This performs the tracing action configured on the given JSTracer: typically
351 : // calling the JSTracer::callback or marking the thing as live.
352 : //
353 : // The argument to JS::TraceEdge is an in-out param: when the function returns,
354 : // the garbage collector might have moved the GC thing. In this case, the
355 : // reference passed to JS::TraceEdge will be updated to the thing's new
356 : // location. Callers of this method are responsible for updating any state that
357 : // is dependent on the object's address. For example, if the object's address
358 : // is used as a key in a hashtable, then the object must be removed and
359 : // re-inserted with the correct hash.
360 : //
361 : // Note that while |edgep| must never be null, it is fine for |*edgep| to be
362 : // nullptr.
363 :
364 : template <typename T>
365 : inline void
366 5246 : TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
367 : {
368 5246 : MOZ_ASSERT(thingp);
369 5246 : if (*thingp)
370 5063 : js::gc::TraceExternalEdge(trc, thingp->unsafeGet(), name);
371 5246 : }
372 :
373 : template <typename T>
374 : inline void
375 474 : TraceEdge(JSTracer* trc, JS::TenuredHeap<T>* thingp, const char* name)
376 : {
377 474 : MOZ_ASSERT(thingp);
378 474 : if (T ptr = thingp->unbarrieredGetPtr()) {
379 462 : js::gc::TraceExternalEdge(trc, &ptr, name);
380 462 : thingp->setPtr(ptr);
381 : }
382 474 : }
383 :
384 : // Edges that are always traced as part of root marking do not require
385 : // incremental barriers. This function allows for marking non-barriered
386 : // pointers, but asserts that this happens during root marking.
387 : //
388 : // Note that while |edgep| must never be null, it is fine for |*edgep| to be
389 : // nullptr.
390 : template <typename T>
391 : extern JS_PUBLIC_API(void)
392 : UnsafeTraceRoot(JSTracer* trc, T* edgep, const char* name);
393 :
394 : extern JS_PUBLIC_API(void)
395 : TraceChildren(JSTracer* trc, GCCellPtr thing);
396 :
397 : using ZoneSet = js::HashSet<Zone*, js::DefaultHasher<Zone*>, js::SystemAllocPolicy>;
398 : using CompartmentSet = js::HashSet<JSCompartment*, js::DefaultHasher<JSCompartment*>,
399 : js::SystemAllocPolicy>;
400 :
401 : /**
402 : * Trace every value within |compartments| that is wrapped by a
403 : * cross-compartment wrapper from a compartment that is not an element of
404 : * |compartments|.
405 : */
406 : extern JS_PUBLIC_API(void)
407 : TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments);
408 :
409 : } // namespace JS
410 :
411 : extern JS_PUBLIC_API(void)
412 : JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc,
413 : void* thing, JS::TraceKind kind, bool includeDetails);
414 :
415 : namespace js {
416 :
417 : // Trace an edge that is not a GC root and is not wrapped in a barriered
418 : // wrapper for some reason.
419 : //
420 : // This method does not check if |*edgep| is non-null before tracing through
421 : // it, so callers must check any nullable pointer before calling this method.
422 : template <typename T>
423 : extern JS_PUBLIC_API(void)
424 : UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name);
425 :
426 : namespace gc {
427 :
428 : // Return true if the given edge is not live and is about to be swept.
429 : template <typename T>
430 : extern JS_PUBLIC_API(bool)
431 : EdgeNeedsSweep(JS::Heap<T>* edgep);
432 :
433 : // Not part of the public API, but declared here so we can use it in GCPolicy
434 : // which is.
435 : template <typename T>
436 : bool
437 : IsAboutToBeFinalizedUnbarriered(T* thingp);
438 :
439 : } // namespace gc
440 : } // namespace js
441 :
442 : #endif /* js_TracingAPI_h */
|