tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */