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
This commit is contained in:
Jon Coppeard 2020-10-05 07:54:40 +00:00
Родитель 2eff6e798b
Коммит 1d5db9bf4c
7 изменённых файлов: 123 добавлений и 117 удалений

Просмотреть файл

@ -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;
}
}
};

Просмотреть файл

@ -74,17 +74,17 @@ template bool DoCallback<JS::PropertyKey>(GenericTracer*, JS::PropertyKey*,
template bool DoCallback<TaggedProto>(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 *****************************************************/

Просмотреть файл

@ -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();

Просмотреть файл

@ -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;

Просмотреть файл

@ -3285,20 +3285,20 @@ JSObject* js::GetThisObjectOfWith(JSObject* env) {
return GetThisObject(env->as<WithEnvironmentObject>().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()) {

Просмотреть файл

@ -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.

Просмотреть файл

@ -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);