TracingAPI.h (14602B)
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/. */ 6 7 #ifndef js_TracingAPI_h 8 #define js_TracingAPI_h 9 10 #include "js/GCTypeMacros.h" 11 #include "js/HeapAPI.h" 12 #include "js/TraceKind.h" 13 14 class JS_PUBLIC_API JSTracer; 15 16 namespace JS { 17 class JS_PUBLIC_API CallbackTracer; 18 template <typename T> 19 class Heap; 20 template <typename T> 21 class TenuredHeap; 22 23 /** Returns a static string equivalent of |kind|. */ 24 JS_PUBLIC_API const char* GCTraceKindToAscii(JS::TraceKind kind); 25 26 /** Returns the base size in bytes of the GC thing of kind |kind|. */ 27 JS_PUBLIC_API size_t GCTraceKindSize(JS::TraceKind kind); 28 29 // Kinds of JSTracer. 30 enum class TracerKind { 31 // Generic tracers: Internal tracers that have a different virtual method 32 // called for each edge kind. 33 Marking, 34 Tenuring, 35 Moving, 36 ClearEdges, 37 Sweeping, 38 MinorSweeping, 39 Barrier, 40 41 // Callback tracers: General-purpose tracers that have a single virtual 42 // method called on every edge. 43 // 44 // Order is important. All callback kinds must follow this one. 45 Callback, 46 47 // Specific kinds of callback tracer. 48 UnmarkGray, 49 VerifyTraceProtoAndIface, 50 CompartmentCheck, 51 HeapCheck 52 }; 53 54 enum class WeakMapTraceAction { 55 /** 56 * Do not trace into weak map keys or values during traversal. Users must 57 * handle weak maps manually. 58 */ 59 Skip, 60 61 /** 62 * Do true ephemeron marking with a weak key lookup marking phase. This is 63 * the default for GCMarker. 64 */ 65 Expand, 66 67 /** 68 * Trace through to all values, irrespective of whether the keys are live 69 * or not. Used for non-marking tracers. 70 */ 71 TraceValues, 72 73 /** 74 * Trace through to all keys and values, irrespective of whether the keys 75 * are live or not. Used for non-marking tracers. 76 */ 77 TraceKeysAndValues 78 }; 79 80 // Whether a tracer should trace weak edges. GCMarker sets this to Skip. 81 enum class WeakEdgeTraceAction { Skip, Trace }; 82 83 struct TraceOptions { 84 JS::WeakMapTraceAction weakMapAction = WeakMapTraceAction::TraceValues; 85 JS::WeakEdgeTraceAction weakEdgeAction = WeakEdgeTraceAction::Trace; 86 87 TraceOptions() = default; 88 TraceOptions(JS::WeakMapTraceAction weakMapActionArg, 89 JS::WeakEdgeTraceAction weakEdgeActionArg) 90 : weakMapAction(weakMapActionArg), weakEdgeAction(weakEdgeActionArg) {} 91 MOZ_IMPLICIT TraceOptions(JS::WeakMapTraceAction weakMapActionArg) 92 : weakMapAction(weakMapActionArg) {} 93 MOZ_IMPLICIT TraceOptions(JS::WeakEdgeTraceAction weakEdgeActionArg) 94 : weakEdgeAction(weakEdgeActionArg) {} 95 }; 96 97 class AutoTracingIndex; 98 99 // Optional context information that can be used to construct human readable 100 // descriptions of what is being traced. 101 class TracingContext { 102 public: 103 // Access to the tracing context: When tracing with a JS::CallbackTracer, we 104 // invoke the callback with the edge location and the type of target. This is 105 // useful for operating on the edge in the abstract or on the target thing, 106 // satisfying most common use cases. However, some tracers need additional 107 // detail about the specific edge that is being traced in order to be 108 // useful. Unfortunately, the raw pointer to the edge that we provide is not 109 // enough information to infer much of anything useful about that edge. 110 // 111 // In order to better support use cases that care in particular about edges -- 112 // as opposed to the target thing -- tracing implementations are responsible 113 // for providing extra context information about each edge they trace, as it 114 // is traced. This contains, at a minimum, an edge name and, when tracing an 115 // array, the index. Further specialization can be achieved (with some 116 // complexity), by associating a functor with the tracer so that, when 117 // requested, the user can generate totally custom edge descriptions. 118 119 // Returns the current edge's index, if marked as part of an array of edges. 120 // This must be called only inside the trace callback. When not tracing an 121 // array, the value will be InvalidIndex. 122 constexpr static size_t InvalidIndex = size_t(-1); 123 size_t index() const { return index_; } 124 125 // Build a description of this edge in the heap graph. This call may invoke 126 // the context functor, if set, which may inspect arbitrary areas of the 127 // heap. On the other hand, the description provided by this method may be 128 // substantially more accurate and useful than those provided by only the 129 // name and index. 130 void getEdgeName(const char* name, char* buffer, size_t bufferSize); 131 132 // The trace implementation may associate a callback with one or more edges 133 // using AutoTracingDetails. This functor is called by getEdgeName and 134 // is responsible for providing a textual representation of the edge currently 135 // being traced. The callback has access to the full heap, including the 136 // currently set tracing context. 137 class Functor { 138 public: 139 virtual void operator()(TracingContext* tcx, const char* name, char* buf, 140 size_t bufsize) = 0; 141 }; 142 143 private: 144 friend class AutoTracingIndex; 145 size_t index_ = InvalidIndex; 146 147 friend class AutoTracingDetails; 148 Functor* functor_ = nullptr; 149 }; 150 151 } // namespace JS 152 153 class JS_PUBLIC_API JSTracer { 154 public: 155 // Return the runtime set on the tracer. 156 JSRuntime* runtime() const { return runtime_; } 157 158 JS::TracerKind kind() const { return kind_; } 159 bool isGenericTracer() const { return kind_ < JS::TracerKind::Callback; } 160 bool isCallbackTracer() const { return kind_ >= JS::TracerKind::Callback; } 161 bool isMarkingTracer() const { return kind_ == JS::TracerKind::Marking; } 162 bool isTenuringTracer() const { return kind_ == JS::TracerKind::Tenuring; } 163 164 inline JS::CallbackTracer* asCallbackTracer(); 165 166 JS::WeakMapTraceAction weakMapAction() const { 167 return options_.weakMapAction; 168 } 169 bool traceWeakEdges() const { 170 return options_.weakEdgeAction == JS::WeakEdgeTraceAction::Trace; 171 } 172 173 JS::TracingContext& context() { return context_; } 174 175 // These methods are called when the tracer encounters an edge. Clients should 176 // override them to receive notifications when an edge of each type is 177 // visited. 178 // 179 // The caller updates the edge with the return value (if different). 180 // 181 // In C++, overriding a method hides all methods in the base class with that 182 // name, not just methods with that signature. Thus, the typed edge methods 183 // have to have distinct names to allow us to override them individually, 184 // which is freqently useful if, for example, we only want to process one type 185 // of edge. 186 #define DEFINE_ON_EDGE_METHOD(name, type, _1, _2) \ 187 virtual void on##name##Edge(type** thingp, const char* name) = 0; 188 JS_FOR_EACH_TRACEKIND(DEFINE_ON_EDGE_METHOD) 189 #undef DEFINE_ON_EDGE_METHOD 190 191 protected: 192 JSTracer(JSRuntime* rt, JS::TracerKind kind, 193 JS::TraceOptions options = JS::TraceOptions()) 194 : runtime_(rt), kind_(kind), options_(options) {} 195 196 private: 197 JSRuntime* const runtime_; 198 const JS::TracerKind kind_; 199 const JS::TraceOptions options_; 200 JS::TracingContext context_; 201 }; 202 203 namespace js { 204 205 // A CRTP helper class that implements a JSTracer by calling a template method 206 // on the derived tracer type for each edge kind. 207 template <typename T> 208 class GenericTracerImpl : public JSTracer { 209 public: 210 GenericTracerImpl(JSRuntime* rt, JS::TracerKind kind, 211 JS::TraceOptions options) 212 : JSTracer(rt, kind, options) {} 213 214 private: 215 T* derived() { return static_cast<T*>(this); } 216 217 #define DEFINE_ON_EDGE_METHOD(name, type, _1, _2) \ 218 void on##name##Edge(type** thingp, const char* name) final { \ 219 derived()->onEdge(thingp, name); \ 220 } 221 JS_FOR_EACH_TRACEKIND(DEFINE_ON_EDGE_METHOD) 222 #undef DEFINE_ON_EDGE_METHOD 223 }; 224 225 } // namespace js 226 227 namespace JS { 228 229 class JS_PUBLIC_API CallbackTracer 230 : public js::GenericTracerImpl<CallbackTracer> { 231 public: 232 CallbackTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Callback, 233 JS::TraceOptions options = JS::TraceOptions()) 234 : GenericTracerImpl(rt, kind, options) { 235 MOZ_ASSERT(isCallbackTracer()); 236 } 237 CallbackTracer(JSContext* cx, JS::TracerKind kind = JS::TracerKind::Callback, 238 JS::TraceOptions options = JS::TraceOptions()); 239 240 // Override this method to receive notification when a node in the GC 241 // heap graph is visited. 242 virtual void onChild(JS::GCCellPtr thing, const char* name) = 0; 243 244 private: 245 template <typename T> 246 void onEdge(T** thingp, const char* name) { 247 onChild(JS::GCCellPtr(*thingp), name); 248 } 249 friend class js::GenericTracerImpl<CallbackTracer>; 250 }; 251 252 // Set the index portion of the tracer's context for the current range. 253 class MOZ_RAII AutoTracingIndex { 254 JSTracer* trc_; 255 256 public: 257 explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(trc) { 258 MOZ_ASSERT(trc_->context().index_ == TracingContext::InvalidIndex); 259 trc_->context().index_ = initial; 260 } 261 ~AutoTracingIndex() { 262 MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex); 263 trc_->context().index_ = TracingContext::InvalidIndex; 264 } 265 266 void operator++() { 267 MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex); 268 ++trc_->context().index_; 269 } 270 }; 271 272 // Set a context callback for the trace callback to use, if it needs a detailed 273 // edge description. 274 class MOZ_RAII AutoTracingDetails { 275 JSTracer* trc_; 276 277 public: 278 AutoTracingDetails(JSTracer* trc, TracingContext::Functor& func) : trc_(trc) { 279 MOZ_ASSERT(trc_->context().functor_ == nullptr); 280 trc_->context().functor_ = &func; 281 } 282 ~AutoTracingDetails() { 283 MOZ_ASSERT(trc_->context().functor_); 284 trc_->context().functor_ = nullptr; 285 } 286 }; 287 288 // Save and clear tracing context when performing nested tracing. 289 class MOZ_RAII AutoClearTracingContext { 290 JSTracer* trc_; 291 TracingContext prev_; 292 293 public: 294 explicit AutoClearTracingContext(JSTracer* trc) 295 : trc_(trc), prev_(trc->context()) { 296 trc_->context() = TracingContext(); 297 } 298 299 ~AutoClearTracingContext() { trc_->context() = prev_; } 300 }; 301 302 } // namespace JS 303 304 JS::CallbackTracer* JSTracer::asCallbackTracer() { 305 MOZ_ASSERT(isCallbackTracer()); 306 return static_cast<JS::CallbackTracer*>(this); 307 } 308 309 namespace js { 310 311 class AbstractGeneratorObject; 312 class SavedFrame; 313 namespace wasm { 314 class AnyRef; 315 } // namespace wasm 316 317 namespace gc { 318 319 #define JS_DECLARE_TRACE_EXTERNAL_EDGE(type) \ 320 extern JS_PUBLIC_API void TraceExternalEdge(JSTracer* trc, type* thingp, \ 321 const char* name); 322 323 // Declare edge-tracing function overloads for public GC pointer types. 324 JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE) 325 JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE) 326 327 #undef JS_DECLARE_TRACE_EXTERNAL_EDGE 328 329 } // namespace gc 330 } // namespace js 331 332 namespace JS { 333 334 // The JS::TraceEdge family of functions traces the given GC thing reference. 335 // This performs the tracing action configured on the given JSTracer: typically 336 // calling the JSTracer::callback or marking the thing as live. 337 // 338 // The argument to JS::TraceEdge is an in-out param: when the function returns, 339 // the garbage collector might have moved the GC thing. In this case, the 340 // reference passed to JS::TraceEdge will be updated to the thing's new 341 // location. Callers of this method are responsible for updating any state that 342 // is dependent on the object's address. For example, if the object's address 343 // is used as a key in a hashtable, then the object must be removed and 344 // re-inserted with the correct hash. 345 // 346 // Note that while |edgep| must never be null, it is fine for |*edgep| to be 347 // nullptr. 348 349 template <typename T> 350 inline void TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name) { 351 MOZ_ASSERT(thingp); 352 if (*thingp) { 353 js::gc::TraceExternalEdge(trc, thingp->unsafeAddress(), name); 354 } 355 } 356 357 template <typename T> 358 inline void TraceEdge(JSTracer* trc, JS::TenuredHeap<T>* thingp, 359 const char* name) { 360 MOZ_ASSERT(thingp); 361 if (T ptr = thingp->unbarrieredGetPtr()) { 362 js::gc::TraceExternalEdge(trc, &ptr, name); 363 thingp->unbarrieredSetPtr(ptr); 364 } 365 } 366 367 // Edges that are always traced as part of root marking do not require 368 // incremental barriers. |JS::TraceRoot| overloads allow for marking 369 // non-barriered pointers but assert that this happens during root marking. 370 // 371 // Note that while |edgep| must never be null, it is fine for |*edgep| to be 372 // nullptr. 373 #define JS_DECLARE_TRACE_ROOT(type) \ 374 extern JS_PUBLIC_API void TraceRoot(JSTracer* trc, type* edgep, \ 375 const char* name); 376 377 // Declare edge-tracing function overloads for public GC pointer types. 378 JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_TRACE_ROOT) 379 JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_TRACE_ROOT) 380 381 // We also require overloads for these purely-internal types. These overloads 382 // ought not be in public headers, and they should use a different name in order 383 // to not be *actual* overloads, but for the moment we still declare them here. 384 JS_DECLARE_TRACE_ROOT(js::AbstractGeneratorObject*) 385 JS_DECLARE_TRACE_ROOT(js::SavedFrame*) 386 JS_DECLARE_TRACE_ROOT(js::wasm::AnyRef) 387 388 #undef JS_DECLARE_TRACE_ROOT 389 390 extern JS_PUBLIC_API void TraceChildren(JSTracer* trc, GCCellPtr thing); 391 392 } // namespace JS 393 394 namespace js { 395 396 inline bool IsTracerKind(JSTracer* trc, JS::TracerKind kind) { 397 return trc->kind() == kind; 398 } 399 400 // Trace an edge that is not a GC root and is not wrapped in a barriered 401 // wrapper for some reason. 402 // 403 // This method does not check if |*edgep| is non-null before tracing through 404 // it, so callers must check any nullable pointer before calling this method. 405 extern JS_PUBLIC_API void UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, 406 JSObject** thingp, 407 const char* name); 408 409 namespace gc { 410 411 // Return true if the given edge is not live and is about to be swept. 412 template <typename T> 413 extern JS_PUBLIC_API bool TraceWeakEdge(JSTracer* trc, JS::Heap<T>* thingp); 414 415 } // namespace gc 416 417 #ifdef DEBUG 418 /* 419 * Return whether the runtime is currently being destroyed, for use in 420 * assertions. 421 */ 422 extern JS_PUBLIC_API bool RuntimeIsBeingDestroyed(); 423 #endif 424 425 } // namespace js 426 427 #endif /* js_TracingAPI_h */