Remove GCZeal: Base cleanup

UXP repo issue #8
This commit is contained in:
wolfbeast 2017-06-26 19:53:53 +02:00 • committed by Roy Tam
commit 97a0673483
18 changed files with 1 additions and 1045 deletions

View file

@ -290,13 +290,6 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
return;
}
#ifdef JS_GC_ZEAL
if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) ||
prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) {
return;
}
#endif
// Context options.
JS::ContextOptions contextOptions;
contextOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
@ -317,37 +310,6 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
}
}
#ifdef JS_GC_ZEAL
void
LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */)
{
AssertIsOnMainThread();
RuntimeService* rts = RuntimeService::GetService();
if (!rts) {
// May be shutting down, just bail.
return;
}
int32_t gczeal = GetWorkerPref<int32_t>(NS_LITERAL_CSTRING(PREF_GCZEAL), -1);
if (gczeal < 0) {
gczeal = 0;
}
int32_t frequency =
GetWorkerPref<int32_t>(NS_LITERAL_CSTRING("gcZeal.frequency"), -1);
if (frequency < 0) {
frequency = JS_DEFAULT_ZEAL_FREQ;
}
RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency));
if (rts) {
rts->UpdateAllWorkerGCZeal();
}
}
#endif
void
UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService,
const nsACString& aPrefName, JSGCParamKey aKey)
@ -994,10 +956,6 @@ InitJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSContext* aWorkerCx)
js::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback);
#ifdef JS_GC_ZEAL
JS_SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency);
#endif
return true;
}
@ -1981,10 +1939,6 @@ RuntimeService::Init()
sDefaultJSSettings.chrome.maxScriptRuntime = -1;
sDefaultJSSettings.chrome.compartmentOptions.behaviors().setVersion(JSVERSION_LATEST);
sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
#ifdef JS_GC_ZEAL
sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
sDefaultJSSettings.gcZeal = 0;
#endif
SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
WORKER_DEFAULT_ALLOCATION_THRESHOLD);
@ -2033,12 +1987,6 @@ RuntimeService::Init()
LoadJSGCMemoryOptions,
PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX,
nullptr)) ||
#ifdef JS_GC_ZEAL
NS_FAILED(Preferences::RegisterCallback(
LoadGCZealOptions,
PREF_JS_OPTIONS_PREFIX PREF_GCZEAL,
nullptr)) ||
#endif
#define WORKER_SIMPLE_PREF(name, getter, NAME) \
NS_FAILED(Preferences::RegisterCallbackAndCall( \
@ -2227,12 +2175,6 @@ RuntimeService::Cleanup()
#undef WORKER_SIMPLE_PREF
#undef WORKER_PREF
#ifdef JS_GC_ZEAL
NS_FAILED(Preferences::UnregisterCallback(
LoadGCZealOptions,
PREF_JS_OPTIONS_PREFIX PREF_GCZEAL,
nullptr)) ||
#endif
NS_FAILED(Preferences::UnregisterCallback(
LoadJSGCMemoryOptions,
PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX,
@ -2644,15 +2586,6 @@ RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,
BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter, aKey, aValue);
}
#ifdef JS_GC_ZEAL
void
RuntimeService::UpdateAllWorkerGCZeal()
{
BROADCAST_ALL_WORKERS(UpdateGCZeal, sDefaultJSSettings.gcZeal,
sDefaultJSSettings.gcZealFrequency);
}
#endif
void
RuntimeService::GarbageCollectAllWorkers(bool aShrinking)
{

View file

@ -215,19 +215,6 @@ public:
void
UpdateAllWorkerMemoryParameter(JSGCParamKey aKey, uint32_t aValue);
#ifdef JS_GC_ZEAL
static void
SetDefaultGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
{
AssertIsOnMainThread();
sDefaultJSSettings.gcZeal = aGCZeal;
sDefaultJSSettings.gcZealFrequency = aFrequency;
}
void
UpdateAllWorkerGCZeal();
#endif
void
GarbageCollectAllWorkers(bool aShrinking);

View file

@ -44,6 +44,3 @@ WORKER_PREF("intl.accept_languages", PrefLanguagesChanged)
WORKER_PREF("general.appname.override", AppNameOverrideChanged)
WORKER_PREF("general.appversion.override", AppVersionOverrideChanged)
WORKER_PREF("general.platform.override", PlatformOverrideChanged)
#ifdef JS_GC_ZEAL
WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
#endif

View file

@ -1391,30 +1391,6 @@ private:
}
};
#ifdef JS_GC_ZEAL
class UpdateGCZealRunnable final : public WorkerControlRunnable
{
uint8_t mGCZeal;
uint32_t mFrequency;
public:
UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
uint8_t aGCZeal,
uint32_t aFrequency)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mGCZeal(aGCZeal), mFrequency(aFrequency)
{ }
private:
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
return true;
}
};
#endif
class GarbageCollectRunnable final : public WorkerControlRunnable
{
bool mShrinking;
@ -3148,27 +3124,6 @@ WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,
}
}
#ifdef JS_GC_ZEAL
template <class Derived>
void
WorkerPrivateParent<Derived>::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
{
AssertIsOnParentThread();
{
MutexAutoLock lock(mMutex);
mJSSettings.gcZeal = aGCZeal;
mJSSettings.gcZealFrequency = aFrequency;
}
RefPtr<UpdateGCZealRunnable> runnable =
new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency);
if (!runnable->Dispatch()) {
NS_WARNING("Failed to update worker gczeal!");
}
}
#endif
template <class Derived>
void
WorkerPrivateParent<Derived>::GarbageCollect(bool aShrinking)
@ -6323,21 +6278,6 @@ WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
}
}
#ifdef JS_GC_ZEAL
void
WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
uint32_t aFrequency)
{
AssertIsOnWorkerThread();
JS_SetGCZeal(aCx, aGCZeal, aFrequency);
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
}
}
#endif
void
WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
bool aCollectChildren)

View file

@ -386,11 +386,6 @@ public:
void
UpdateJSWorkerMemoryParameter(JSGCParamKey key, uint32_t value);
#ifdef JS_GC_ZEAL
void
UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency);
#endif
void
GarbageCollect(bool aShrinking);

View file

@ -143,15 +143,7 @@ struct JSSettings
JSGCSettingsArray gcSettings;
JS::ContextOptions contextOptions;
#ifdef JS_GC_ZEAL
uint8_t gcZeal;
uint32_t gcZealFrequency;
#endif
JSSettings()
#ifdef JS_GC_ZEAL
: gcZeal(0), gcZealFrequency(0)
#endif
{
for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) {
new (gcSettings + index) JSGCSetting();

View file

@ -188,11 +188,7 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp)
if (!JS_SetProperty(cx, info, "tsan", value))
return false;
#ifdef JS_GC_ZEAL
value = BooleanValue(true);
#else
value = BooleanValue(false);
#endif
if (!JS_SetProperty(cx, info, "has-gczeal", value))
return false;
@ -731,171 +727,6 @@ GCPreserveCode(JSContext* cx, unsigned argc, Value* vp)
return true;
}
#ifdef JS_GC_ZEAL
static bool
GCZeal(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 2) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
uint32_t zeal;
if (!ToUint32(cx, args.get(0), &zeal))
return false;
if (zeal > uint32_t(gc::ZealMode::Limit)) {
JS_ReportErrorASCII(cx, "gczeal argument out of range");
return false;
}
uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
if (args.length() >= 2) {
if (!ToUint32(cx, args.get(1), &frequency))
return false;
}
JS_SetGCZeal(cx, (uint8_t)zeal, frequency);
args.rval().setUndefined();
return true;
}
static bool
ScheduleGC(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 1) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
if (args.length() == 0) {
/* Fetch next zeal trigger only. */
} else if (args[0].isInt32()) {
/* Schedule a GC to happen after |arg| allocations. */
JS_ScheduleGC(cx, args[0].toInt32());
} else if (args[0].isObject()) {
/* Ensure that |zone| is collected during the next GC. */
Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
PrepareZoneForGC(zone);
} else if (args[0].isString()) {
/* This allows us to schedule the atoms zone for GC. */
Zone* zone = args[0].toString()->zoneFromAnyThread();
if (!CurrentThreadCanAccessZone(zone)) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
return false;
}
PrepareZoneForGC(zone);
} else {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Bad argument - expecting integer, object or string");
return false;
}
uint32_t zealBits;
uint32_t freq;
uint32_t next;
JS_GetGCZealBits(cx, &zealBits, &freq, &next);
args.rval().setInt32(next);
return true;
}
static bool
SelectForGC(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/*
* The selectedForMarking set is intended to be manually marked at slice
* start to detect missing pre-barriers. It is invalid for nursery things
* to be in the set, so evict the nursery before adding items.
*/
JSRuntime* rt = cx->runtime();
rt->gc.evictNursery();
for (unsigned i = 0; i < args.length(); i++) {
if (args[i].isObject()) {
if (!rt->gc.selectForMarking(&args[i].toObject()))
return false;
}
}
args.rval().setUndefined();
return true;
}
static bool
VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 0) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
args.rval().setUndefined();
return true;
}
static bool
VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp)
{
// This is a no-op since the post barrier verifier was removed.
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length()) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
args.rval().setUndefined();
return true;
}
static bool
GCState(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 0) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
const char* state = StateName(cx->runtime()->gc.state());
JSString* str = JS_NewStringCopyZ(cx, state);
if (!str)
return false;
args.rval().setString(str);
return true;
}
static bool
DeterministicGC(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
return false;
}
cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
args.rval().setUndefined();
return true;
}
#endif /* JS_GC_ZEAL */
static bool
StartGC(JSContext* cx, unsigned argc, Value* vp)
{
@ -4387,39 +4218,6 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
"gcPreserveCode()",
" Preserve JIT code during garbage collections."),
#ifdef JS_GC_ZEAL
JS_FN_HELP("gczeal", GCZeal, 2, 0,
"gczeal(level, [N])",
gc::ZealModeHelpText),
JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
"schedulegc([num | obj | string])",
" If num is given, schedule a GC after num allocations.\n"
" If obj is given, schedule a GC of obj's zone.\n"
" If string is given, schedule a GC of the string's zone if possible.\n"
" Returns the number of allocations before the next trigger."),
JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
"selectforgc(obj1, obj2, ...)",
" Schedule the given objects to be marked in the next GC slice."),
JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
"verifyprebarriers()",
" Start or end a run of the pre-write barrier verifier."),
JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
"verifypostbarriers()",
" Does nothing (the post-write barrier verifier has been remove)."),
JS_FN_HELP("gcstate", GCState, 0, 0,
"gcstate()",
" Report the global GC state."),
JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
"deterministicgc(true|false)",
" If true, only allow determinstic GCs to run."),
#endif
JS_FN_HELP("startgc", StartGC, 1, 0,
"startgc([n [, 'shrinking']])",
" Start an incremental GC and run a slice that processes about n objects.\n"

View file

@ -3,7 +3,6 @@
"optimize": true,
"debug": true,
"env": {
"JS_GC_ZEAL": "Compact",
"JITTEST_EXTRA_ARGS": "--jitflags=debug --ignore-timeouts={DIR}/cgc-jittest-timeouts.txt",
"JSTESTS_EXTRA_ARGS": "--exclude-file={DIR}/cgc-jstests-slow.txt"
},

View file

@ -3,7 +3,6 @@
"optimize": true,
"debug": true,
"env": {
"JS_GC_ZEAL": "GenerationalGC",
"JSTESTS_EXTRA_ARGS": "--jitflags=debug"
}
}

View file

@ -190,7 +190,7 @@ GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind)
return false;
}
#if defined(JS_GC_ZEAL) || defined(DEBUG)
#if defined(DEBUG)
MOZ_ASSERT_IF(cx->compartment()->isAtomsCompartment(),
kind == AllocKind::ATOM ||
kind == AllocKind::FAT_INLINE_ATOM ||
@ -223,11 +223,6 @@ GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind)
bool
GCRuntime::gcIfNeededPerAllocation(JSContext* cx)
{
#ifdef JS_GC_ZEAL
if (needZealousGC())
runDebugGC();
#endif
// Invoking the interrupt callback can fail and we can't usefully
// handle that here. Just check in case we need to collect instead.
if (rt->hasPendingInterrupt())

View file

@ -61,50 +61,10 @@ class MOZ_RAII AutoPrepareForTracing
AbortReason
IsIncrementalGCUnsafe(JSRuntime* rt);
#ifdef JS_GC_ZEAL
class MOZ_RAII AutoStopVerifyingBarriers
{
GCRuntime* gc;
bool restartPreVerifier;
public:
AutoStopVerifyingBarriers(JSRuntime* rt, bool isShutdown)
: gc(&rt->gc)
{
if (gc->isVerifyPreBarriersEnabled()) {
gc->endVerifyPreBarriers();
restartPreVerifier = !isShutdown;
} else {
restartPreVerifier = false;
}
}
~AutoStopVerifyingBarriers() {
// Nasty special case: verification runs a minor GC, which *may* nest
// inside of an outer minor GC. This is not allowed by the
// gc::Statistics phase tree. So we pause the "real" GC, if in fact one
// is in progress.
gcstats::Phase outer = gc->stats.currentPhase();
if (outer != gcstats::PHASE_NONE)
gc->stats.endPhase(outer);
MOZ_ASSERT((gc->stats.currentPhase() == gcstats::PHASE_NONE) ||
(gc->stats.currentPhase() == gcstats::PHASE_GC_BEGIN) ||
(gc->stats.currentPhase() == gcstats::PHASE_GC_END));
if (restartPreVerifier)
gc->startVerifyPreBarriers();
if (outer != gcstats::PHASE_NONE)
gc->stats.beginPhase(outer);
}
};
#else
struct MOZ_RAII AutoStopVerifyingBarriers
{
AutoStopVerifyingBarriers(JSRuntime*, bool) {}
};
#endif /* JS_GC_ZEAL */
#ifdef JSGC_HASH_TABLE_CHECKS
void CheckHashTablesAfterMovingGC(JSRuntime* rt);

View file

@ -653,19 +653,6 @@ class GCRuntime
void onOutOfMallocMemory();
void onOutOfMallocMemory(const AutoLockGC& lock);
#ifdef JS_GC_ZEAL
const void* addressOfZealModeBits() { return &zealModeBits; }
void getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
void setZeal(uint8_t zeal, uint32_t frequency);
bool parseAndSetZeal(const char* str);
void setNextScheduled(uint32_t count);
void verifyPreBarriers();
void maybeVerifyPreBarriers(bool always);
bool selectForMarking(JSObject* object);
void clearSelectedForMarking();
void setDeterministic(bool enable);
#endif
size_t maxMallocBytesAllocated() { return maxMallocBytes; }
uint64_t nextCellUniqueId() {
@ -851,14 +838,7 @@ class GCRuntime
AutoMaybeStartBackgroundAllocation& maybeStartBGAlloc);
void recycleChunk(Chunk* chunk, const AutoLockGC& lock);
#ifdef JS_GC_ZEAL
void startVerifyPreBarriers();
void endVerifyPreBarriers();
void finishVerifier();
bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; }
#else
bool isVerifyPreBarriersEnabled() const { return false; }
#endif
// Free certain LifoAlloc blocks when it is safe to do so.
void freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo);
@ -1219,10 +1199,6 @@ class GCRuntime
ZoneList zonesToMaybeCompact;
Arena* relocatedArenasToRelease;
#ifdef JS_GC_ZEAL
MarkingValidator* markingValidator;
#endif
/*
* Indicates that a GC slice has taken place in the middle of an animation
* frame, rather than at the beginning. In this case, the next slice will be
@ -1274,40 +1250,6 @@ class GCRuntime
bool poked;
/*
* These options control the zealousness of the GC. At every allocation,
* nextScheduled is decremented. When it reaches zero we do a full GC.
*
* At this point, if zeal_ is one of the types that trigger periodic
* collection, then nextScheduled is reset to the value of zealFrequency.
* Otherwise, no additional GCs take place.
*
* You can control these values in several ways:
* - Set the JS_GC_ZEAL environment variable
* - Call gczeal() or schedulegc() from inside shell-executed JS code
* (see the help for details)
*
* If gcZeal_ == 1 then we perform GCs in select places (during MaybeGC and
* whenever a GC poke happens). This option is mainly useful to embedders.
*
* We use zeal_ == 4 to enable write barrier verification. See the comment
* in jsgc.cpp for more information about this.
*
* zeal_ values from 8 to 10 periodically run different types of
* incremental GC.
*
* zeal_ value 14 performs periodic shrinking collections.
*/
#ifdef JS_GC_ZEAL
uint32_t zealModeBits;
int zealFrequency;
int nextScheduled;
bool deterministicOnly;
int incrementalLimit;
Vector<JSObject*, 0, SystemAllocPolicy> selectedForMarking;
#endif
bool fullCompartmentChecks;
Callback<JSGCCallback> gcCallback;
@ -1415,50 +1357,10 @@ class MOZ_RAII AutoMaybeStartBackgroundAllocation
}
};
#ifdef JS_GC_ZEAL
inline bool
GCRuntime::hasZealMode(ZealMode mode)
{
static_assert(size_t(ZealMode::Limit) < sizeof(zealModeBits) * 8,
"Zeal modes must fit in zealModeBits");
return zealModeBits & (1 << uint32_t(mode));
}
inline void
GCRuntime::clearZealMode(ZealMode mode)
{
zealModeBits &= ~(1 << uint32_t(mode));
MOZ_ASSERT(!hasZealMode(mode));
}
inline bool
GCRuntime::upcomingZealousGC() {
return nextScheduled == 1;
}
inline bool
GCRuntime::needZealousGC() {
if (nextScheduled > 0 && --nextScheduled == 0) {
if (hasZealMode(ZealMode::Alloc) ||
hasZealMode(ZealMode::GenerationalGC) ||
hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
hasZealMode(ZealMode::IncrementalMultipleSlices) ||
hasZealMode(ZealMode::Compact))
{
nextScheduled = zealFrequency;
}
return true;
}
return false;
}
#else
inline bool GCRuntime::hasZealMode(ZealMode mode) { return false; }
inline void GCRuntime::clearZealMode(ZealMode mode) { }
inline bool GCRuntime::upcomingZealousGC() { return false; }
inline bool GCRuntime::needZealousGC() { return false; }
#endif
} /* namespace gc */

View file

@ -1322,22 +1322,6 @@ TenuredCell::writeBarrierPre(TenuredCell* thing)
if (!thing)
return;
#ifdef JS_GC_ZEAL
// When verifying pre barriers we need to switch on all barriers, even
// those on the Atoms Zone. Normally, we never enter a parse task when
// collecting in the atoms zone, so will filter out atoms below.
// Unfortuantely, If we try that when verifying pre-barriers, we'd never be
// able to handle OMT parse tasks at all as we switch on the verifier any
// time we're not doing GC. This would cause us to deadlock, as OMT parsing
// is meant to resume after GC work completes. Instead we filter out any
// OMT barriers that reach us and assert that they would normally not be
// possible.
if (!CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread())) {
AssertSafeToSkipBarrier(thing);
return;
}
#endif
JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
if (shadowZone->needsIncrementalBarrier()) {
MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));

View file

@ -74,14 +74,6 @@ struct js::Nursery::SweepAction
#endif
};
#ifdef JS_GC_ZEAL
struct js::Nursery::Canary
{
uintptr_t magicValue;
Canary* next;
};
#endif
inline void
js::Nursery::NurseryChunk::poisonAndInit(JSRuntime* rt, uint8_t poison)
{
@ -124,9 +116,6 @@ js::Nursery::Nursery(JSRuntime* rt)
, minorGcCount_(0)
, freeMallocedBuffersTask(nullptr)
, sweepActions_(nullptr)
#ifdef JS_GC_ZEAL
, lastCanary_(nullptr)
#endif
{}
bool
@ -209,10 +198,6 @@ js::Nursery::enable()
setCurrentChunk(0);
setStartPosition();
#ifdef JS_GC_ZEAL
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
enterZealMode();
#endif
MOZ_ALWAYS_TRUE(runtime()->gc.storeBuffer.enable());
return;
@ -243,23 +228,6 @@ js::Nursery::isEmpty() const
return position() == currentStartPosition_;
}
#ifdef JS_GC_ZEAL
void
js::Nursery::enterZealMode() {
if (isEnabled())
updateNumChunks(maxNurseryChunks_);
}
void
js::Nursery::leaveZealMode() {
if (isEnabled()) {
MOZ_ASSERT(isEmpty());
setCurrentChunk(0);
setStartPosition();
}
}
#endif // JS_GC_ZEAL
JSObject*
js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp)
{
@ -305,12 +273,6 @@ js::Nursery::allocate(size_t size)
MOZ_ASSERT(position() % gc::CellSize == 0);
MOZ_ASSERT(size % gc::CellSize == 0);
#ifdef JS_GC_ZEAL
static const size_t CanarySize = (sizeof(Nursery::Canary) + CellSize - 1) & ~CellMask;
if (runtime()->gc.hasZealMode(ZealMode::CheckNursery))
size += CanarySize;
#endif
if (currentEnd() < position() + size) {
if (currentChunk_ + 1 == numChunks())
return nullptr;
@ -322,19 +284,6 @@ js::Nursery::allocate(size_t size)
JS_EXTRA_POISON(thing, JS_ALLOCATED_NURSERY_PATTERN, size);
#ifdef JS_GC_ZEAL
if (runtime()->gc.hasZealMode(ZealMode::CheckNursery)) {
auto canary = reinterpret_cast<Canary*>(position() - CanarySize);
canary->magicValue = CanaryMagicValue;
canary->next = nullptr;
if (lastCanary_) {
MOZ_ASSERT(!lastCanary_->next);
lastCanary_->next = canary;
}
lastCanary_ = canary;
}
#endif
MemProfiler::SampleNursery(reinterpret_cast<void*>(thing), size);
return thing;
}
@ -561,14 +510,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
rt->gc.incMinorGcNumber();
#ifdef JS_GC_ZEAL
if (rt->gc.hasZealMode(ZealMode::CheckNursery)) {
for (auto canary = lastCanary_; canary; canary = canary->next)
MOZ_ASSERT(canary->magicValue == CanaryMagicValue);
}
lastCanary_ = nullptr;
#endif
rt->gc.stats.beginNurseryCollection(reason);
TraceMinorGCStart();
@ -752,10 +693,6 @@ js::Nursery::doCollection(JSRuntime* rt, JS::gcreason::Reason reason,
// Make sure hashtables have been updated after the collection.
maybeStartProfile(ProfileKey::CheckHashTables);
#ifdef JS_GC_ZEAL
if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
CheckHashTablesAfterMovingGC(rt);
#endif
maybeEndProfile(ProfileKey::CheckHashTables);
// Calculate and return the promotion rate.
@ -828,17 +765,6 @@ js::Nursery::sweep()
runSweepActions();
sweepDictionaryModeObjects();
#ifdef JS_GC_ZEAL
/* Poison the nursery contents so touching a freed object will crash. */
for (unsigned i = 0; i < numChunks(); i++)
chunk(i).poisonAndInit(runtime(), JS_SWEPT_NURSERY_PATTERN);
if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
/* Only reset the alloc point when we are close to the end. */
if (currentChunk_ + 1 == numChunks())
setCurrentChunk(0);
} else
#endif
{
#ifdef JS_CRASH_DIAGNOSTICS
for (unsigned i = 0; i < numChunks(); ++i)
@ -916,20 +842,12 @@ js::Nursery::growAllocableSpace()
void
js::Nursery::shrinkAllocableSpace()
{
#ifdef JS_GC_ZEAL
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
return;
#endif
updateNumChunks(Max(numChunks() - 1, 1u));
}
void
js::Nursery::minimizeAllocableSpace()
{
#ifdef JS_GC_ZEAL
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
return;
#endif
updateNumChunks(1);
}

View file

@ -252,11 +252,6 @@ class Nursery
(numChunks() - currentChunk_ - 1) * NurseryChunkUsableSize;
}
#ifdef JS_GC_ZEAL
void enterZealMode();
void leaveZealMode();
#endif
/* Print total profile times on shutdown. */
void printTotalProfileTimes();
@ -374,11 +369,6 @@ class Nursery
using NativeObjectVector = Vector<NativeObject*, 0, SystemAllocPolicy>;
NativeObjectVector dictionaryModeObjects_;
#ifdef JS_GC_ZEAL
struct Canary;
Canary* lastCanary_;
#endif
NurseryChunk* allocChunk();
NurseryChunk& chunk(unsigned index) const {

View file

@ -26,420 +26,6 @@
using namespace js;
using namespace js::gc;
#ifdef JS_GC_ZEAL
/*
* Write barrier verification
*
* The next few functions are for write barrier verification.
*
* The VerifyBarriers function is a shorthand. It checks if a verification phase
* is currently running. If not, it starts one. Otherwise, it ends the current
* phase and starts a new one.
*
* The user can adjust the frequency of verifications, which causes
* VerifyBarriers to be a no-op all but one out of N calls. However, if the
* |always| parameter is true, it starts a new phase no matter what.
*
* Pre-Barrier Verifier:
* When StartVerifyBarriers is called, a snapshot is taken of all objects in
* the GC heap and saved in an explicit graph data structure. Later,
* EndVerifyBarriers traverses the heap again. Any pointer values that were in
* the snapshot and are no longer found must be marked; otherwise an assertion
* triggers. Note that we must not GC in between starting and finishing a
* verification phase.
*/
struct EdgeValue
{
void* thing;
JS::TraceKind kind;
const char* label;
};
struct VerifyNode
{
void* thing;
JS::TraceKind kind;
uint32_t count;
EdgeValue edges[1];
};
typedef HashMap<void*, VerifyNode*, DefaultHasher<void*>, SystemAllocPolicy> NodeMap;
/*
* The verifier data structures are simple. The entire graph is stored in a
* single block of memory. At the beginning is a VerifyNode for the root
* node. It is followed by a sequence of EdgeValues--the exact number is given
* in the node. After the edges come more nodes and their edges.
*
* The edgeptr and term fields are used to allocate out of the block of memory
* for the graph. If we run out of memory (i.e., if edgeptr goes beyond term),
* we just abandon the verification.
*
* The nodemap field is a hashtable that maps from the address of the GC thing
* to the VerifyNode that represents it.
*/
class js::VerifyPreTracer final : public JS::CallbackTracer
{
JS::AutoDisableGenerationalGC noggc;
void onChild(const JS::GCCellPtr& thing) override;
public:
/* The gcNumber when the verification began. */
uint64_t number;
/* This counts up to gcZealFrequency to decide whether to verify. */
int count;
/* This graph represents the initial GC "snapshot". */
VerifyNode* curnode;
VerifyNode* root;
char* edgeptr;
char* term;
NodeMap nodemap;
explicit VerifyPreTracer(JSRuntime* rt)
: JS::CallbackTracer(rt), noggc(rt), number(rt->gc.gcNumber()), count(0), curnode(nullptr),
root(nullptr), edgeptr(nullptr), term(nullptr)
{}
~VerifyPreTracer() {
js_free(root);
}
};
/*
* This function builds up the heap snapshot by adding edges to the current
* node.
*/
void
VerifyPreTracer::onChild(const JS::GCCellPtr& thing)
{
MOZ_ASSERT(!IsInsideNursery(thing.asCell()));
// Skip things in other runtimes.
if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime())
return;
edgeptr += sizeof(EdgeValue);
if (edgeptr >= term) {
edgeptr = term;
return;
}
VerifyNode* node = curnode;
uint32_t i = node->count;
node->edges[i].thing = thing.asCell();
node->edges[i].kind = thing.kind();
node->edges[i].label = contextName();
node->count++;
}
static VerifyNode*
MakeNode(VerifyPreTracer* trc, void* thing, JS::TraceKind kind)
{
NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing);
if (!p) {
VerifyNode* node = (VerifyNode*)trc->edgeptr;
trc->edgeptr += sizeof(VerifyNode) - sizeof(EdgeValue);
if (trc->edgeptr >= trc->term) {
trc->edgeptr = trc->term;
return nullptr;
}
node->thing = thing;
node->count = 0;
node->kind = kind;
if (!trc->nodemap.add(p, thing, node)) {
trc->edgeptr = trc->term;
return nullptr;
}
return node;
}
return nullptr;
}
static VerifyNode*
NextNode(VerifyNode* node)
{
if (node->count == 0)
return (VerifyNode*)((char*)node + sizeof(VerifyNode) - sizeof(EdgeValue));
else
return (VerifyNode*)((char*)node + sizeof(VerifyNode) +
sizeof(EdgeValue)*(node->count - 1));
}
void
gc::GCRuntime::startVerifyPreBarriers()
{
if (verifyPreData || isIncrementalGCInProgress())
return;
if (IsIncrementalGCUnsafe(rt) != AbortReason::None)
return;
number++;
VerifyPreTracer* trc = js_new<VerifyPreTracer>(rt);
if (!trc)
return;
AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
for (auto chunk = allNonEmptyChunks(); !chunk.done(); chunk.next())
chunk->bitmap.clear();
gcstats::AutoPhase ap(stats, gcstats::PHASE_TRACE_HEAP);
const size_t size = 64 * 1024 * 1024;
trc->root = (VerifyNode*)js_malloc(size);
if (!trc->root)
goto oom;
trc->edgeptr = (char*)trc->root;
trc->term = trc->edgeptr + size;
if (!trc->nodemap.init())
goto oom;
/* Create the root node. */
trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
incrementalState = State::MarkRoots;
/* Make all the roots be edges emanating from the root node. */
traceRuntime(trc, prep.session().lock);
VerifyNode* node;
node = trc->curnode;
if (trc->edgeptr == trc->term)
goto oom;
/* For each edge, make a node for it if one doesn't already exist. */
while ((char*)node < trc->edgeptr) {
for (uint32_t i = 0; i < node->count; i++) {
EdgeValue& e = node->edges[i];
VerifyNode* child = MakeNode(trc, e.thing, e.kind);
if (child) {
trc->curnode = child;
js::TraceChildren(trc, e.thing, e.kind);
}
if (trc->edgeptr == trc->term)
goto oom;
}
node = NextNode(node);
}
verifyPreData = trc;
incrementalState = State::Mark;
marker.start();
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
PurgeJITCaches(zone);
if (!zone->usedByExclusiveThread) {
zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit);
zone->arenas.purge();
}
}
return;
oom:
incrementalState = State::NotActive;
js_delete(trc);
verifyPreData = nullptr;
}
static bool
IsMarkedOrAllocated(TenuredCell* cell)
{
return cell->isMarked() || cell->arena()->allocatedDuringIncremental;
}
struct CheckEdgeTracer : public JS::CallbackTracer {
VerifyNode* node;
explicit CheckEdgeTracer(JSRuntime* rt) : JS::CallbackTracer(rt), node(nullptr) {}
void onChild(const JS::GCCellPtr& thing) override;
};
static const uint32_t MAX_VERIFIER_EDGES = 1000;
/*
* This function is called by EndVerifyBarriers for every heap edge. If the edge
* already existed in the original snapshot, we "cancel it out" by overwriting
* it with nullptr. EndVerifyBarriers later asserts that the remaining
* non-nullptr edges (i.e., the ones from the original snapshot that must have
* been modified) must point to marked objects.
*/
void
CheckEdgeTracer::onChild(const JS::GCCellPtr& thing)
{
// Skip things in other runtimes.
if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime())
return;
/* Avoid n^2 behavior. */
if (node->count > MAX_VERIFIER_EDGES)
return;
for (uint32_t i = 0; i < node->count; i++) {
if (node->edges[i].thing == thing.asCell()) {
MOZ_ASSERT(node->edges[i].kind == thing.kind());
node->edges[i].thing = nullptr;
return;
}
}
}
void
js::gc::AssertSafeToSkipBarrier(TenuredCell* thing)
{
Zone* zone = thing->zoneFromAnyThread();
MOZ_ASSERT(!zone->needsIncrementalBarrier() || zone->isAtomsZone());
}
static bool
IsMarkedOrAllocated(const EdgeValue& edge)
{
if (!edge.thing || IsMarkedOrAllocated(TenuredCell::fromPointer(edge.thing)))
return true;
// Permanent atoms and well-known symbols aren't marked during graph traversal.
if (edge.kind == JS::TraceKind::String && static_cast<JSString*>(edge.thing)->isPermanentAtom())
return true;
if (edge.kind == JS::TraceKind::Symbol && static_cast<JS::Symbol*>(edge.thing)->isWellKnownSymbol())
return true;
return false;
}
void
gc::GCRuntime::endVerifyPreBarriers()
{
VerifyPreTracer* trc = verifyPreData;
if (!trc)
return;
MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt));
AutoPrepareForTracing prep(rt->contextFromMainThread(), SkipAtoms);
bool compartmentCreated = false;
/* We need to disable barriers before tracing, which may invoke barriers. */
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
if (!zone->needsIncrementalBarrier())
compartmentCreated = true;
zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit);
PurgeJITCaches(zone);
}
/*
* We need to bump gcNumber so that the methodjit knows that jitcode has
* been discarded.
*/
MOZ_ASSERT(trc->number == number);
number++;
verifyPreData = nullptr;
incrementalState = State::NotActive;
if (!compartmentCreated && IsIncrementalGCUnsafe(rt) == AbortReason::None) {
CheckEdgeTracer cetrc(rt);
/* Start after the roots. */
VerifyNode* node = NextNode(trc->root);
while ((char*)node < trc->edgeptr) {
cetrc.node = node;
js::TraceChildren(&cetrc, node->thing, node->kind);
if (node->count <= MAX_VERIFIER_EDGES) {
for (uint32_t i = 0; i < node->count; i++) {
EdgeValue& edge = node->edges[i];
if (!IsMarkedOrAllocated(edge)) {
char msgbuf[1024];
SprintfLiteral(msgbuf,
"[barrier verifier] Unmarked edge: %s %p '%s' edge to %s %p",
JS::GCTraceKindToAscii(node->kind), node->thing,
edge.label,
JS::GCTraceKindToAscii(edge.kind), edge.thing);
MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
MOZ_CRASH();
}
}
}
node = NextNode(node);
}
}
marker.reset();
marker.stop();
js_delete(trc);
}
/*** Barrier Verifier Scheduling ***/
void
gc::GCRuntime::verifyPreBarriers()
{
if (verifyPreData)
endVerifyPreBarriers();
else
startVerifyPreBarriers();
}
void
gc::VerifyBarriers(JSRuntime* rt, VerifierType type)
{
if (type == PreBarrierVerifier)
rt->gc.verifyPreBarriers();
}
void
gc::GCRuntime::maybeVerifyPreBarriers(bool always)
{
if (!hasZealMode(ZealMode::VerifierPre))
return;
if (rt->mainThread.suppressGC)
return;
if (verifyPreData) {
if (++verifyPreData->count < zealFrequency && !always)
return;
endVerifyPreBarriers();
}
startVerifyPreBarriers();
}
void
js::gc::MaybeVerifyBarriers(JSContext* cx, bool always)
{
GCRuntime* gc = &cx->runtime()->gc;
gc->maybeVerifyPreBarriers(always);
}
void
js::gc::GCRuntime::finishVerifier()
{
if (verifyPreData) {
js_delete(verifyPreData);
verifyPreData = nullptr;
}
}
#endif /* JS_GC_ZEAL */
#ifdef JSGC_HASH_TABLE_CHECKS
class CheckHeapTracer : public JS::CallbackTracer

View file

@ -5761,19 +5761,6 @@ JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const JS::CallAr
/************************************************************************/
#ifdef JS_GC_ZEAL
#define JS_DEFAULT_ZEAL_FREQ 100
extern JS_PUBLIC_API(void)
JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
extern JS_PUBLIC_API(void)
JS_SetGCZeal(JSContext* cx, uint8_t zeal, uint32_t frequency);
extern JS_PUBLIC_API(void)
JS_ScheduleGC(JSContext* cx, uint32_t count);
#endif
extern JS_PUBLIC_API(void)
JS_SetParallelParsingEnabled(JSContext* cx, bool enabled);

View file

@ -42,12 +42,6 @@ inline void
GCRuntime::poke()
{
poked = true;
#ifdef JS_GC_ZEAL
/* Schedule a GC to happen "soon" after a GC poke. */
if (hasZealMode(ZealMode::Poke))
nextScheduled = 1;
#endif
}
class ArenaIter