Mypal/js/src/gc/RootMarking.cpp

543 lines
18 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#ifdef MOZ_VALGRIND
# include <valgrind/memcheck.h>
#endif
#include "jscntxt.h"
#include "jsgc.h"
#include "jsprf.h"
#include "jstypes.h"
#include "builtin/MapObject.h"
#include "frontend/BytecodeCompiler.h"
#include "gc/GCInternals.h"
#include "gc/Marking.h"
#include "jit/MacroAssembler.h"
#include "js/HashTable.h"
#include "vm/Debugger.h"
#include "vm/JSONParser.h"
#include "jsgcinlines.h"
#include "jsobjinlines.h"
using namespace js;
using namespace js::gc;
using mozilla::ArrayEnd;
using JS::AutoGCRooter;
typedef RootedValueMap::Range RootRange;
typedef RootedValueMap::Entry RootEntry;
typedef RootedValueMap::Enum RootEnum;
template <typename T>
using TraceFunction = void (*)(JSTracer* trc, T* ref, const char* name);
// For more detail see JS::Rooted::ptr and js::DispatchWrapper.
//
// The JS::RootKind::Traceable list contains a bunch of totally disparate
// types, but the instantiations of DispatchWrapper below need /something/ in
// the type field. We use the following type as a compatible stand-in. No
// actual methods from ConcreteTraceable type are actually used at runtime --
// the real trace function has been stored inline in the DispatchWrapper.
struct ConcreteTraceable {
ConcreteTraceable() { MOZ_CRASH("instantiation of ConcreteTraceable"); }
void trace(JSTracer*) {}
};
template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot>
static inline void
MarkExactStackRootList(JSTracer* trc, JS::Rooted<void*>* rooter, const char* name)
{
while (rooter) {
T* addr = reinterpret_cast<JS::Rooted<T>*>(rooter)->address();
TraceFn(trc, addr, name);
rooter = rooter->previous();
}
}
static inline void
TraceStackRoots(JSTracer* trc, RootedListHeads& stackRoots)
{
#define MARK_ROOTS(name, type, _) \
MarkExactStackRootList<type*>(trc, stackRoots[JS::RootKind::name], "exact-" #name);
JS_FOR_EACH_TRACEKIND(MARK_ROOTS)
#undef MARK_ROOTS
MarkExactStackRootList<jsid>(trc, stackRoots[JS::RootKind::Id], "exact-id");
MarkExactStackRootList<Value>(trc, stackRoots[JS::RootKind::Value], "exact-value");
MarkExactStackRootList<ConcreteTraceable,
js::DispatchWrapper<ConcreteTraceable>::TraceWrapped>(
trc, stackRoots[JS::RootKind::Traceable], "Traceable");
}
void
js::RootLists::traceStackRoots(JSTracer* trc)
{
TraceStackRoots(trc, stackRoots_);
}
static void
MarkExactStackRoots(JSRuntime* rt, JSTracer* trc)
{
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next())
TraceStackRoots(trc, zone->stackRoots_);
rt->contextFromMainThread()->roots.traceStackRoots(trc);
}
template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot>
static inline void
MarkPersistentRootedList(JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list,
const char* name)
{
for (PersistentRooted<void*>* r : list)
TraceFn(trc, reinterpret_cast<PersistentRooted<T>*>(r)->address(), name);
}
void
js::RootLists::tracePersistentRoots(JSTracer* trc)
{
#define MARK_ROOTS(name, type, _) \
MarkPersistentRootedList<type*>(trc, heapRoots_[JS::RootKind::name], "persistent-" #name);
JS_FOR_EACH_TRACEKIND(MARK_ROOTS)
#undef MARK_ROOTS
MarkPersistentRootedList<jsid>(trc, heapRoots_[JS::RootKind::Id], "persistent-id");
MarkPersistentRootedList<Value>(trc, heapRoots_[JS::RootKind::Value], "persistent-value");
MarkPersistentRootedList<ConcreteTraceable,
js::DispatchWrapper<ConcreteTraceable>::TraceWrapped>(trc,
heapRoots_[JS::RootKind::Traceable], "persistent-traceable");
}
static void
MarkPersistentRooted(JSRuntime* rt, JSTracer* trc)
{
rt->contextFromMainThread()->roots.tracePersistentRoots(trc);
}
template <typename T>
static void
FinishPersistentRootedChain(mozilla::LinkedList<PersistentRooted<void*>>& listArg)
{
auto& list = reinterpret_cast<mozilla::LinkedList<PersistentRooted<T>>&>(listArg);
while (!list.isEmpty())
list.getFirst()->reset();
}
void
js::RootLists::finishPersistentRoots()
{
#define FINISH_ROOT_LIST(name, type, _) \
FinishPersistentRootedChain<type*>(heapRoots_[JS::RootKind::name]);
JS_FOR_EACH_TRACEKIND(FINISH_ROOT_LIST)
#undef FINISH_ROOT_LIST
FinishPersistentRootedChain<jsid>(heapRoots_[JS::RootKind::Id]);
FinishPersistentRootedChain<Value>(heapRoots_[JS::RootKind::Value]);
// Note that we do not finalize the Traceable list as we do not know how to
// safely clear memebers. We instead assert that none escape the RootLists.
// See the comment on RootLists::~RootLists for details.
}
inline void
AutoGCRooter::trace(JSTracer* trc)
{
switch (tag_) {
case PARSER:
frontend::MarkParser(trc, this);
return;
case VALARRAY: {
/*
* We don't know the template size parameter, but we can safely treat it
* as an AutoValueArray<1> because the length is stored separately.
*/
AutoValueArray<1>* array = static_cast<AutoValueArray<1>*>(this);
TraceRootRange(trc, array->length(), array->begin(), "js::AutoValueArray");
return;
}
case IONMASM: {
static_cast<js::jit::MacroAssembler::AutoRooter*>(this)->masm()->trace(trc);
return;
}
case WRAPPER: {
/*
* We need to use TraceManuallyBarrieredEdge here because we mark
* wrapper roots in every slice. This is because of some rule-breaking
* in RemapAllWrappersForObject; see comment there.
*/
TraceManuallyBarrieredEdge(trc, &static_cast<AutoWrapperRooter*>(this)->value.get(),
"JS::AutoWrapperRooter.value");
return;
}
case WRAPVECTOR: {
AutoWrapperVector::VectorImpl& vector = static_cast<AutoWrapperVector*>(this)->vector;
/*
* We need to use TraceManuallyBarrieredEdge here because we mark
* wrapper roots in every slice. This is because of some rule-breaking
* in RemapAllWrappersForObject; see comment there.
*/
for (WrapperValue* p = vector.begin(); p < vector.end(); p++)
TraceManuallyBarrieredEdge(trc, &p->get(), "js::AutoWrapperVector.vector");
return;
}
case CUSTOM:
static_cast<JS::CustomAutoRooter*>(this)->trace(trc);
return;
}
MOZ_ASSERT(tag_ >= 0);
if (Value* vp = static_cast<AutoArrayRooter*>(this)->array)
TraceRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
}
/* static */ void
AutoGCRooter::traceAll(JSTracer* trc)
{
for (AutoGCRooter* gcr = trc->runtime()->contextFromMainThread()->roots.autoGCRooters_; gcr; gcr = gcr->down)
gcr->trace(trc);
}
/* static */ void
AutoGCRooter::traceAllWrappers(JSTracer* trc)
{
JSContext* cx = trc->runtime()->contextFromMainThread();
for (AutoGCRooter* gcr = cx->roots.autoGCRooters_; gcr; gcr = gcr->down) {
if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
gcr->trace(trc);
}
}
void
StackShape::trace(JSTracer* trc)
{
if (base)
TraceRoot(trc, &base, "StackShape base");
TraceRoot(trc, (jsid*) &propid, "StackShape id");
if ((attrs & JSPROP_GETTER) && rawGetter)
TraceRoot(trc, (JSObject**)&rawGetter, "StackShape getter");
if ((attrs & JSPROP_SETTER) && rawSetter)
TraceRoot(trc, (JSObject**)&rawSetter, "StackShape setter");
}
void
PropertyDescriptor::trace(JSTracer* trc)
{
if (obj)
TraceRoot(trc, &obj, "Descriptor::obj");
TraceRoot(trc, &value, "Descriptor::value");
if ((attrs & JSPROP_GETTER) && getter) {
JSObject* tmp = JS_FUNC_TO_DATA_PTR(JSObject*, getter);
TraceRoot(trc, &tmp, "Descriptor::get");
getter = JS_DATA_TO_FUNC_PTR(JSGetterOp, tmp);
}
if ((attrs & JSPROP_SETTER) && setter) {
JSObject* tmp = JS_FUNC_TO_DATA_PTR(JSObject*, setter);
TraceRoot(trc, &tmp, "Descriptor::set");
setter = JS_DATA_TO_FUNC_PTR(JSSetterOp, tmp);
}
}
void
js::gc::GCRuntime::traceRuntimeForMajorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock)
{
// FinishRoots will have asserted that every root that we do not expect
// is gone, so we can simply skip traceRuntime here.
if (rt->isBeingDestroyed())
return;
gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
if (rt->atomsCompartment(lock)->zone()->isCollecting())
traceRuntimeAtoms(trc, lock);
JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(trc);
traceRuntimeCommon(trc, MarkRuntime, lock);
}
void
js::gc::GCRuntime::traceRuntimeForMinorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock)
{
// Note that we *must* trace the runtime during the SHUTDOWN_GC's minor GC
// despite having called FinishRoots already. This is because FinishRoots
// does not clear the crossCompartmentWrapper map. It cannot do this
// because Proxy's trace for CrossCompartmentWrappers asserts presence in
// the map. And we can reach its trace function despite having finished the
// roots via the edges stored by the pre-barrier verifier when we finish
// the verifier for the last time.
gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
// FIXME: As per bug 1298816 comment 12, we should be able to remove this.
jit::JitRuntime::MarkJitcodeGlobalTableUnconditionally(trc);
traceRuntimeCommon(trc, TraceRuntime, lock);
}
void
js::TraceRuntime(JSTracer* trc)
{
MOZ_ASSERT(!trc->isMarkingTracer());
JSRuntime* rt = trc->runtime();
rt->gc.evictNursery();
AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
rt->gc.traceRuntime(trc, prep.session().lock);
}
void
js::gc::GCRuntime::traceRuntime(JSTracer* trc, AutoLockForExclusiveAccess& lock)
{
MOZ_ASSERT(!rt->isBeingDestroyed());
gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
traceRuntimeAtoms(trc, lock);
traceRuntimeCommon(trc, TraceRuntime, lock);
}
void
js::gc::GCRuntime::traceRuntimeAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
{
gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_RUNTIME_DATA);
MarkPermanentAtoms(trc);
MarkAtoms(trc, lock);
MarkWellKnownSymbols(trc);
jit::JitRuntime::Mark(trc, lock);
}
void
js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
AutoLockForExclusiveAccess& lock)
{
MOZ_ASSERT(!rt->mainThread.suppressGC);
{
gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_STACK);
// Trace active interpreter and JIT stack roots.
MarkInterpreterActivations(rt, trc);
jit::MarkJitActivations(rt, trc);
// Trace legacy C stack roots.
AutoGCRooter::traceAll(trc);
for (RootRange r = rootsHash.all(); !r.empty(); r.popFront()) {
const RootEntry& entry = r.front();
TraceRoot(trc, entry.key(), entry.value());
}
// Trace C stack roots.
MarkExactStackRoots(rt, trc);
}
// Trace runtime global roots.
MarkPersistentRooted(rt, trc);
// Trace the self-hosting global compartment.
rt->markSelfHostingGlobal(trc);
// Trace the shared Intl data.
rt->traceSharedIntlData(trc);
// Trace anything in the single context. Note that this is actually the
// same struct as the JSRuntime, but is still split for historical reasons.
rt->contextFromMainThread()->mark(trc);
// Trace all compartment roots, but not the compartment itself; it is
// marked via the parent pointer if traceRoots actually traces anything.
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
c->traceRoots(trc, traceOrMark);
// Trace SPS.
rt->spsProfiler.trace(trc);
// Trace helper thread roots.
HelperThreadState().trace(trc);
// Trace the embedding's black and gray roots.
if (!rt->isHeapMinorCollecting()) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING);
/*
* The embedding can register additional roots here.
*
* We don't need to trace these in a minor GC because all pointers into
* the nursery should be in the store buffer, and we want to avoid the
* time taken to trace all these roots.
*/
for (size_t i = 0; i < blackRootTracers.length(); i++) {
const Callback<JSTraceDataOp>& e = blackRootTracers[i];
(*e.op)(trc, e.data);
}
/* During GC, we don't mark gray roots at this stage. */
if (JSTraceDataOp op = grayRootTracer.op) {
if (traceOrMark == TraceRuntime)
(*op)(trc, grayRootTracer.data);
}
}
}
#ifdef DEBUG
class AssertNoRootsTracer : public JS::CallbackTracer
{
void onChild(const JS::GCCellPtr& thing) override {
MOZ_CRASH("There should not be any roots after finishRoots");
}
public:
AssertNoRootsTracer(JSRuntime* rt, WeakMapTraceKind weakTraceKind)
: JS::CallbackTracer(rt, weakTraceKind)
{}
};
#endif // DEBUG
void
js::gc::GCRuntime::finishRoots()
{
rt->finishAtoms();
if (rootsHash.initialized())
rootsHash.clear();
rt->contextFromMainThread()->roots.finishPersistentRoots();
rt->finishSelfHosting();
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
c->finishRoots();
#ifdef DEBUG
// The nsWrapperCache may not be empty before our shutdown GC, so we have
// to skip that table when verifying that we are fully unrooted.
auto prior = grayRootTracer;
grayRootTracer = Callback<JSTraceDataOp>(nullptr, nullptr);
AssertNoRootsTracer trc(rt, TraceWeakMapKeysValues);
AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
traceRuntime(&trc, prep.session().lock);
// Restore the wrapper tracing so that we leak instead of leaving dangling
// pointers.
grayRootTracer = prior;
#endif // DEBUG
}
// Append traced things to a buffer on the zone for use later in the GC.
// See the comment in GCRuntime.h above grayBufferState for details.
class BufferGrayRootsTracer : public JS::CallbackTracer
{
// Set to false if we OOM while buffering gray roots.
bool bufferingGrayRootsFailed;
void onChild(const JS::GCCellPtr& thing) override;
public:
explicit BufferGrayRootsTracer(JSRuntime* rt)
: JS::CallbackTracer(rt), bufferingGrayRootsFailed(false)
{}
bool failed() const { return bufferingGrayRootsFailed; }
#ifdef DEBUG
TracerKind getTracerKind() const override { return TracerKind::GrayBuffering; }
#endif
};
#ifdef DEBUG
// Return true if this trace is happening on behalf of gray buffering during
// the marking phase of incremental GC.
bool
js::IsBufferGrayRootsTracer(JSTracer* trc)
{
return trc->isCallbackTracer() &&
trc->asCallbackTracer()->getTracerKind() == JS::CallbackTracer::TracerKind::GrayBuffering;
}
#endif
void
js::gc::GCRuntime::bufferGrayRoots()
{
// Precondition: the state has been reset to "unused" after the last GC
// and the zone's buffers have been cleared.
MOZ_ASSERT(grayBufferState == GrayBufferState::Unused);
for (GCZonesIter zone(rt); !zone.done(); zone.next())
MOZ_ASSERT(zone->gcGrayRoots.empty());
gcstats::AutoPhase ap(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS);
BufferGrayRootsTracer grayBufferer(rt);
if (JSTraceDataOp op = grayRootTracer.op)
(*op)(&grayBufferer, grayRootTracer.data);
// Propagate the failure flag from the marker to the runtime.
if (grayBufferer.failed()) {
grayBufferState = GrayBufferState::Failed;
resetBufferedGrayRoots();
} else {
grayBufferState = GrayBufferState::Okay;
}
}
struct SetMaybeAliveFunctor {
template <typename T> void operator()(T* t) { SetMaybeAliveFlag(t); }
};
void
BufferGrayRootsTracer::onChild(const JS::GCCellPtr& thing)
{
MOZ_ASSERT(runtime()->isHeapBusy());
MOZ_RELEASE_ASSERT(thing);
// Check if |thing| is corrupt by calling a method that touches the heap.
MOZ_RELEASE_ASSERT(thing.asCell()->getTraceKind() <= JS::TraceKind::Null);
if (bufferingGrayRootsFailed)
return;
gc::TenuredCell* tenured = gc::TenuredCell::fromPointer(thing.asCell());
Zone* zone = tenured->zone();
if (zone->isCollecting()) {
// See the comment on SetMaybeAliveFlag to see why we only do this for
// objects and scripts. We rely on gray root buffering for this to work,
// but we only need to worry about uncollected dead compartments during
// incremental GCs (when we do gray root buffering).
DispatchTyped(SetMaybeAliveFunctor(), thing);
if (!zone->gcGrayRoots.append(tenured))
bufferingGrayRootsFailed = true;
}
}
void
GCRuntime::markBufferedGrayRoots(JS::Zone* zone)
{
MOZ_ASSERT(grayBufferState == GrayBufferState::Okay);
MOZ_ASSERT(zone->isGCMarkingGray() || zone->isGCCompacting());
for (auto cell : zone->gcGrayRoots)
TraceManuallyBarrieredGenericPointerEdge(&marker, &cell, "buffered gray root");
}
void
GCRuntime::resetBufferedGrayRoots() const
{
MOZ_ASSERT(grayBufferState != GrayBufferState::Okay,
"Do not clear the gray buffers unless we are Failed or becoming Unused");
for (GCZonesIter zone(rt); !zone.done(); zone.next())
zone->gcGrayRoots.clearAndFree();
}