From 1d5db9bf4c0feb89b289f1ce0d0f308badbda681 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Mon, 5 Oct 2020 07:54:40 +0000 Subject: [PATCH] Bug 1668825 - Split out tracing context information into a separate class r=sfink This gives JSTracer a nullable pointer to a JS::TracingContext. This will only be set for CallbackTracers. Differential Revision: https://phabricator.services.mozilla.com/D92256 --- js/public/TracingAPI.h | 206 +++++++++++++------------ js/src/gc/Tracer.cpp | 12 +- js/src/gc/Verifier.cpp | 6 +- js/src/util/DumpFunctions.cpp | 2 +- js/src/vm/JSObject.cpp | 10 +- js/src/vm/UbiNode.cpp | 2 +- xpcom/base/CycleCollectedJSRuntime.cpp | 2 +- 7 files changed, 123 insertions(+), 117 deletions(-) diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index 7019d2c52190..2d2a489008f6 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -86,6 +86,71 @@ enum class WeakMapTraceAction { TraceKeysAndValues }; +class AutoTracingName; +class AutoTracingIndex; +class AutoTracingCallback; + +// 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 name. It is only valid to call this when + // inside the trace callback, however, the edge name will always be set. + const char* name() const { + MOZ_ASSERT(name_); + return name_; + } + + // 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(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 AutoTracingName; + const char* name_ = nullptr; + + friend class AutoTracingIndex; + size_t index_ = InvalidIndex; + + friend class AutoTracingDetails; + Functor* functor_ = nullptr; +}; + } // namespace JS namespace js { @@ -97,6 +162,8 @@ class JS_PUBLIC_API JSTracer { // Return the runtime set on the tracer. JSRuntime* runtime() const { return runtime_; } + JS::TracingContext* maybeContext() { return maybeContext_; } + JS::TracerKind kind() const { return kind_; } bool isMarkingTracer() const { return kind_ == JS::TracerKind::Marking; } bool isTenuringTracer() const { return kind_ == JS::TracerKind::Tenuring; } @@ -125,6 +192,8 @@ class JS_PUBLIC_API JSTracer { JS::WeakMapTraceAction::TraceValues) : runtime_(rt), kind_(kind), weakMapAction_(weakMapAction) {} + void setContext(JS::TracingContext* tcx) { maybeContext_ = tcx; } + void setTraceWeakEdges(bool value) { traceWeakEdges_ = value; } // If this is set to false, then the tracer will skip some jsids @@ -138,6 +207,7 @@ class JS_PUBLIC_API JSTracer { private: JSRuntime* const runtime_; + JS::TracingContext* maybeContext_ = nullptr; const JS::TracerKind kind_; const JS::WeakMapTraceAction weakMapAction_; @@ -153,10 +223,6 @@ class JS_PUBLIC_API JSTracer { namespace js { -class AutoTracingName; -class AutoTracingIndex; -class AutoTracingCallback; - class GenericTracer : public JSTracer { public: GenericTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Generic, @@ -212,79 +278,27 @@ class GenericTracer : public JSTracer { namespace JS { -class AutoTracingName; -class AutoTracingIndex; -class AutoTracingCallback; - class JS_PUBLIC_API CallbackTracer : public js::GenericTracer { public: CallbackTracer( JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Callback, WeakMapTraceAction weakMapAction = WeakMapTraceAction::TraceValues) - : GenericTracer(rt, kind, weakMapAction), - contextName_(nullptr), - contextIndex_(InvalidIndex), - contextFunctor_(nullptr) { + : GenericTracer(rt, kind, weakMapAction) { MOZ_ASSERT(isCallbackTracer()); + setContext(&context_); } CallbackTracer( JSContext* cx, JS::TracerKind kind = JS::TracerKind::Callback, WeakMapTraceAction weakMapAction = WeakMapTraceAction::TraceValues); + TracingContext& context() { return context_; } + // Override this method to receive notification when a node in the GC // heap graph is visited. // This method should return whether the edge still exists, i.e. return false // if 'thing' is cleared by the CallbackTracer, return true otherwise. virtual bool onChild(const JS::GCCellPtr& thing) = 0; - // 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 achived - // (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 name. It is only valid to call this when - // inside the trace callback, however, the edge name will always be set. - const char* contextName() const { - MOZ_ASSERT(contextName_); - return contextName_; - } - - // 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. - const static size_t InvalidIndex = size_t(-1); - size_t contextIndex() const { return contextIndex_; } - - // 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 - // contextName and contextIndex. - void getTracingEdgeName(char* buffer, size_t bufferSize); - - // The trace implementation may associate a callback with one or more edges - // using AutoTracingDetails. This functor is called by getTracingEdgeName - // and is responsible for providing a textual representation of the - // currently being traced edge. The callback has access to the full heap, - // including the currently set tracing context. - class ContextFunctor { - public: - virtual void operator()(CallbackTracer* trc, char* buf, size_t bufsize) = 0; - }; - private: // This class implements the GenericTracer interface to dispatches to onChild. virtual bool onObjectEdge(JSObject** objp) { @@ -321,61 +335,54 @@ class JS_PUBLIC_API CallbackTracer : public js::GenericTracer { return onChild(JS::GCCellPtr(*sharedp, JS::TraceKind::RegExpShared)); } - friend class AutoTracingName; - const char* contextName_; - - friend class AutoTracingIndex; - size_t contextIndex_; - - friend class AutoTracingDetails; - ContextFunctor* contextFunctor_; + TracingContext context_; }; // Set the name portion of the tracer's context for the current edge. class MOZ_RAII AutoTracingName { - CallbackTracer* trc_; - const char* prior_; + TracingContext* tcx_ = nullptr; + const char* prior_ = nullptr; public: - AutoTracingName(JSTracer* trc, const char* name) : trc_(nullptr) { + AutoTracingName(JSTracer* trc, const char* name) { MOZ_ASSERT(name); - if (trc->isCallbackTracer()) { - trc_ = trc->asCallbackTracer(); - prior_ = trc_->contextName_; - trc_->contextName_ = name; + if (TracingContext* tcx = trc->maybeContext()) { + tcx_ = tcx; + prior_ = tcx_->name_; + tcx_->name_ = name; } } ~AutoTracingName() { - if (trc_) { - MOZ_ASSERT(trc_->contextName_); - trc_->contextName_ = prior_; + if (tcx_) { + MOZ_ASSERT(tcx_->name_); + tcx_->name_ = prior_; } } }; // Set the index portion of the tracer's context for the current range. class MOZ_RAII AutoTracingIndex { - CallbackTracer* trc_; + TracingContext* tcx_ = nullptr; public: - explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(nullptr) { - if (trc->isCallbackTracer()) { - trc_ = trc->asCallbackTracer(); - MOZ_ASSERT(trc_->contextIndex_ == CallbackTracer::InvalidIndex); - trc_->contextIndex_ = initial; + explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) { + if (TracingContext* tcx = trc->maybeContext()) { + tcx_ = tcx; + MOZ_ASSERT(tcx_->index_ == TracingContext::InvalidIndex); + tcx_->index_ = initial; } } ~AutoTracingIndex() { - if (trc_) { - MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex); - trc_->contextIndex_ = CallbackTracer::InvalidIndex; + if (tcx_) { + MOZ_ASSERT(tcx_->index_ != TracingContext::InvalidIndex); + tcx_->index_ = TracingContext::InvalidIndex; } } void operator++() { - if (trc_) { - MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex); - ++trc_->contextIndex_; + if (tcx_) { + MOZ_ASSERT(tcx_->index_ != TracingContext::InvalidIndex); + ++tcx_->index_; } } }; @@ -383,21 +390,20 @@ class MOZ_RAII AutoTracingIndex { // Set a context callback for the trace callback to use, if it needs a detailed // edge description. class MOZ_RAII AutoTracingDetails { - CallbackTracer* trc_; + TracingContext* tcx_ = nullptr; public: - AutoTracingDetails(JSTracer* trc, CallbackTracer::ContextFunctor& func) - : trc_(nullptr) { - if (trc->isCallbackTracer()) { - trc_ = trc->asCallbackTracer(); - MOZ_ASSERT(trc_->contextFunctor_ == nullptr); - trc_->contextFunctor_ = &func; + AutoTracingDetails(JSTracer* trc, TracingContext::Functor& func) { + if (TracingContext* tcx = trc->maybeContext()) { + tcx_ = tcx; + MOZ_ASSERT(tcx_->functor_ == nullptr); + tcx_->functor_ = &func; } } ~AutoTracingDetails() { - if (trc_) { - MOZ_ASSERT(trc_->contextFunctor_); - trc_->contextFunctor_ = nullptr; + if (tcx_) { + MOZ_ASSERT(tcx_->functor_); + tcx_->functor_ = nullptr; } } }; diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index 7119f55388e7..88c62439544e 100644 --- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -74,17 +74,17 @@ template bool DoCallback(GenericTracer*, JS::PropertyKey*, template bool DoCallback(GenericTracer*, TaggedProto*, const char*); -void JS::CallbackTracer::getTracingEdgeName(char* buffer, size_t bufferSize) { +void JS::TracingContext::getEdgeName(char* buffer, size_t bufferSize) { MOZ_ASSERT(bufferSize > 0); - if (contextFunctor_) { - (*contextFunctor_)(this, buffer, bufferSize); + if (functor_) { + (*functor_)(this, buffer, bufferSize); return; } - if (contextIndex_ != InvalidIndex) { - snprintf(buffer, bufferSize, "%s[%zu]", contextName_, contextIndex_); + if (index_ != InvalidIndex) { + snprintf(buffer, bufferSize, "%s[%zu]", name_, index_); return; } - snprintf(buffer, bufferSize, "%s", contextName_); + snprintf(buffer, bufferSize, "%s", name_); } /*** Public Tracing API *****************************************************/ diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index 9aca2d0a0104..2f6c57b207c3 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -144,7 +144,7 @@ bool VerifyPreTracer::onChild(const JS::GCCellPtr& thing) { uint32_t i = node->count; node->edges[i].thing = thing; - node->edges[i].label = contextName(); + node->edges[i].label = context().name(); node->count++; return true; } @@ -834,7 +834,7 @@ bool HeapCheckTracerBase::onChild(const JS::GCCellPtr& thing) { return true; } - WorkItem item(thing, contextName(), parentIndex); + WorkItem item(thing, context().name(), parentIndex); if (!stack.append(item)) { oom = true; } @@ -879,7 +879,7 @@ void HeapCheckTracerBase::dumpCellInfo(Cell* cell) { } void HeapCheckTracerBase::dumpCellPath() { - const char* name = contextName(); + const char* name = context().name(); for (int index = parentIndex; index != -1; index = stack[index].parentIndex) { const WorkItem& parent = stack[index]; Cell* cell = parent.thing.asCell(); diff --git a/js/src/util/DumpFunctions.cpp b/js/src/util/DumpFunctions.cpp index f42392d9c42f..398cf2b15efc 100644 --- a/js/src/util/DumpFunctions.cpp +++ b/js/src/util/DumpFunctions.cpp @@ -591,7 +591,7 @@ bool DumpHeapTracer::onChild(const JS::GCCellPtr& thing) { } char buffer[1024]; - getTracingEdgeName(buffer, sizeof(buffer)); + context().getEdgeName(buffer, sizeof(buffer)); fprintf(output, "%s%p %c %s\n", prefix, thing.asCell(), MarkDescriptor(thing.asCell()), buffer); return true; diff --git a/js/src/vm/JSObject.cpp b/js/src/vm/JSObject.cpp index a0cb530ec1bc..cefd9600a6b3 100644 --- a/js/src/vm/JSObject.cpp +++ b/js/src/vm/JSObject.cpp @@ -3285,20 +3285,20 @@ JSObject* js::GetThisObjectOfWith(JSObject* env) { return GetThisObject(env->as().withThis()); } -class GetObjectSlotNameFunctor : public JS::CallbackTracer::ContextFunctor { +class GetObjectSlotNameFunctor : public JS::TracingContext::Functor { JSObject* obj; public: explicit GetObjectSlotNameFunctor(JSObject* ctx) : obj(ctx) {} - virtual void operator()(JS::CallbackTracer* trc, char* buf, + virtual void operator()(JS::TracingContext* trc, char* buf, size_t bufsize) override; }; -void GetObjectSlotNameFunctor::operator()(JS::CallbackTracer* trc, char* buf, +void GetObjectSlotNameFunctor::operator()(JS::TracingContext* tcx, char* buf, size_t bufsize) { - MOZ_ASSERT(trc->contextIndex() != JS::CallbackTracer::InvalidIndex); + MOZ_ASSERT(tcx->index() != JS::TracingContext::InvalidIndex); - uint32_t slot = uint32_t(trc->contextIndex()); + uint32_t slot = uint32_t(tcx->index()); Shape* shape; if (obj->isNative()) { diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp index a564495f70b4..48828fd798b3 100644 --- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -214,7 +214,7 @@ class EdgeVectorTracer final : public JS::CallbackTracer { if (wantNames) { // Ask the tracer to compute an edge name for us. char buffer[1024]; - getTracingEdgeName(buffer, sizeof(buffer)); + context().getEdgeName(buffer, sizeof(buffer)); const char* name = buffer; // Convert the name to char16_t characters. diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index fd55979812a0..4b8ca962e476 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -421,7 +421,7 @@ bool TraversalTracer::onChild(const JS::GCCellPtr& aThing) { if (JS::IsCCTraceKind(aThing.kind())) { if (MOZ_UNLIKELY(mCb.WantDebugInfo())) { char buffer[200]; - getTracingEdgeName(buffer, sizeof(buffer)); + context().getEdgeName(buffer, sizeof(buffer)); mCb.NoteNextEdgeName(buffer); } mCb.NoteJSChild(aThing);