mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-26 09:07:30 +09:00
parent
97a4b8e1e1
commit
97a0673483
18 changed files with 1 additions and 1045 deletions
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
"optimize": true,
|
||||
"debug": true,
|
||||
"env": {
|
||||
"JS_GC_ZEAL": "GenerationalGC",
|
||||
"JSTESTS_EXTRA_ARGS": "--jitflags=debug"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue