Bug 938124 - Add classes for use in IonBuilder thread safety analysis, r=jandem.

This commit is contained in:
Brian Hackett 2013-11-27 16:28:57 -07:00
parent b354a123b3
commit 80ea5ead55
6 changed files with 163 additions and 1 deletions

View File

@ -1114,6 +1114,32 @@ InFreeList(ArenaHeader *aheader, void *thing)
}
} /* namespace gc */
// Ion compilation mainly occurs off the main thread, and may run while the
// main thread is performing arbitrary VM operations, excepting GC activity.
// The below class is used to mark functions and other operations which can
// safely be performed off thread without racing. When running with thread
// safety checking on, any access to a GC thing outside of AutoThreadSafeAccess
// will cause an access violation.
class AutoThreadSafeAccess
{
public:
#if defined(DEBUG) && !defined(XP_WIN)
JSRuntime *runtime;
gc::ArenaHeader *arena;
AutoThreadSafeAccess(const gc::Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
~AutoThreadSafeAccess();
#else
AutoThreadSafeAccess(const gc::Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoThreadSafeAccess() {}
#endif
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
} /* namespace js */
#endif /* gc_Heap_h */

View File

@ -1669,6 +1669,15 @@ IonCompile(JSContext *cx, JSScript *script,
RootedScript builderScript(cx, builder->script());
IonSpewNewFunction(graph, builderScript);
Maybe<AutoProtectHeapForCompilation> protect;
if (js_IonOptions.checkThreadSafety &&
cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL &&
!cx->runtime()->profilingScripts &&
!cx->runtime()->spsProfiler.enabled())
{
protect.construct(cx->runtime());
}
bool succeeded = builder->build();
builder->clearForBackEnd();
@ -1704,6 +1713,9 @@ IonCompile(JSContext *cx, JSScript *script,
return AbortReason_Disable;
}
if (!protect.empty())
protect.destroy();
bool success = codegen->link(cx, builder->constraints());
IonSpewEndFunction();

View File

@ -83,6 +83,12 @@ struct IonOptions
// Default: false
bool checkRangeAnalysis;
// Whether to protect the GC heap during Ion compilation and ensure that
// only threadsafe operations are performed on it.
//
// Default: false
bool checkThreadSafety;
// Whether to perform expensive graph-consistency DEBUG-only assertions.
// It can be useful to disable this to reduce DEBUG-compile time of large
// asm.js programs.
@ -221,6 +227,7 @@ struct IonOptions
edgeCaseAnalysis(true),
rangeAnalysis(true),
checkRangeAnalysis(false),
checkThreadSafety(false),
assertGraphConsistency(true),
uce(true),
eaa(true),

View File

@ -5467,6 +5467,9 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
if (op->getBoolOption("ion-check-range-analysis"))
jit::js_IonOptions.checkRangeAnalysis = true;
if (op->getBoolOption("ion-check-thread-safety"))
jit::js_IonOptions.checkThreadSafety = true;
if (const char *str = op->getStringOption("ion-inlining")) {
if (strcmp(str, "on") == 0)
jit::js_IonOptions.inlining = true;
@ -5775,6 +5778,8 @@ main(int argc, char **argv, char **envp)
"Range analysis (default: on, off to disable)")
|| !op.addBoolOption('\0', "ion-check-range-analysis",
"Range analysis checking")
|| !op.addBoolOption('\0', "ion-check-thread-safety",
"IonBuilder thread safety checking")
|| !op.addStringOption('\0', "ion-inlining", "on/off",
"Inline methods where possible (default: on, off to disable)")
|| !op.addStringOption('\0', "ion-osr", "on/off",
@ -5868,8 +5873,15 @@ main(int argc, char **argv, char **envp)
if (!JS_Init())
return 1;
// When doing thread safety checks for VM accesses made during Ion compilation,
// we rely on protected memory and only the main thread should be active.
JSUseHelperThreads useHelperThreads =
op.getBoolOption("ion-check-thread-safety")
? JS_NO_HELPER_THREADS
: JS_USE_HELPER_THREADS;
/* Use the same parameters as the browser in xpcjsruntime.cpp. */
rt = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS);
rt = JS_NewRuntime(32L * 1024L * 1024L, useHelperThreads);
if (!rt)
return 1;
gTimeoutFunc = NullValue();

View File

@ -15,6 +15,10 @@
#include <locale.h>
#include <string.h>
#if defined(DEBUG) && !defined(XP_WIN)
# include <sys/mman.h>
#endif
#include "jsatom.h"
#include "jsdtoa.h"
#include "jsgc.h"
@ -266,6 +270,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
decimalSeparator(0),
numGrouping(0),
#endif
heapProtected_(false),
mathCache_(nullptr),
activeCompilations_(0),
keepAtoms_(0),
@ -802,6 +807,76 @@ JSRuntime::activeGCInAtomsZone()
return zone->needsBarrier() || zone->isGCScheduled() || zone->wasGCStarted();
}
#if defined(DEBUG) && !defined(XP_WIN)
AutoProtectHeapForCompilation::AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: runtime(rt)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
JS_ASSERT(!runtime->heapProtected_);
runtime->heapProtected_ = true;
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) {
Chunk *chunk = r.front();
// Note: Don't protect the last page in the chunk, which stores
// immutable info and needs to be accessible for runtimeFromAnyThread()
// in AutoThreadSafeAccess.
if (mprotect(chunk, ChunkSize - sizeof(Arena), PROT_NONE))
MOZ_CRASH();
}
}
AutoProtectHeapForCompilation::~AutoProtectHeapForCompilation()
{
JS_ASSERT(runtime->heapProtected_);
JS_ASSERT(runtime->unprotectedArenas.empty());
runtime->heapProtected_ = false;
for (GCChunkSet::Range r(runtime->gcChunkSet.all()); !r.empty(); r.popFront()) {
Chunk *chunk = r.front();
if (mprotect(chunk, ChunkSize - sizeof(Arena), PROT_READ | PROT_WRITE))
MOZ_CRASH();
}
}
AutoThreadSafeAccess::AutoThreadSafeAccess(const Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: runtime(cell->runtimeFromAnyThread()), arena(nullptr)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (!runtime->heapProtected_)
return;
ArenaHeader *base = cell->arenaHeader();
for (size_t i = 0; i < runtime->unprotectedArenas.length(); i++) {
if (base == runtime->unprotectedArenas[i])
return;
}
arena = base;
if (mprotect(arena, sizeof(Arena), PROT_READ))
MOZ_CRASH();
if (!runtime->unprotectedArenas.append(arena))
MOZ_CRASH();
}
AutoThreadSafeAccess::~AutoThreadSafeAccess()
{
if (!arena)
return;
if (mprotect(arena, sizeof(Arena), PROT_NONE))
MOZ_CRASH();
JS_ASSERT(arena == runtime->unprotectedArenas.back());
runtime->unprotectedArenas.popBack();
}
#endif // DEBUG && !XP_WIN
#ifdef JS_WORKER_THREADS
void

View File

@ -679,6 +679,7 @@ class MarkingValidator;
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
class AutoLockForExclusiveAccess;
class AutoProtectHeapForCompilation;
void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
@ -1432,6 +1433,18 @@ struct JSRuntime : public JS::shadow::Runtime,
const char *numGrouping;
#endif
friend class js::AutoProtectHeapForCompilation;
friend class js::AutoThreadSafeAccess;
mozilla::DebugOnly<bool> heapProtected_;
#ifdef DEBUG
js::Vector<js::gc::ArenaHeader *, 0, js::SystemAllocPolicy> unprotectedArenas;
public:
bool heapProtected() {
return heapProtected_;
}
#endif
private:
js::MathCache *mathCache_;
js::MathCache *createMathCache(JSContext *cx);
@ -2023,6 +2036,23 @@ class RuntimeAllocPolicy
extern const JSSecurityCallbacks NullSecurityCallbacks;
class AutoProtectHeapForCompilation
{
public:
#if defined(DEBUG) && !defined(XP_WIN)
JSRuntime *runtime;
AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
~AutoProtectHeapForCompilation();
#else
AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
#endif
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
} /* namespace js */
#ifdef _MSC_VER