Bug 1087178 - OdinMonkey: explain why 'not stored in cache' (r=bbouvier,janv)

--HG--
extra : rebase_source : d37893913a09d5a5b339e4ae4ee42f873d449d1e
This commit is contained in:
Luke Wagner 2014-10-22 17:28:07 -05:00
parent 63551eb571
commit 4f56ee7daf
10 changed files with 146 additions and 76 deletions

View File

@ -375,7 +375,7 @@ public:
}
};
bool
JS::AsmJSCacheResult
BlockUntilOpen(AutoClose* aCloser)
{
MOZ_ASSERT(!mWaiting, "Can only call BlockUntilOpen once");
@ -384,7 +384,9 @@ public:
mWaiting = true;
nsresult rv = NS_DispatchToMainThread(this);
NS_ENSURE_SUCCESS(rv, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return JS::AsmJSCache_InternalError;
}
{
MutexAutoLock lock(mMutex);
@ -394,7 +396,7 @@ public:
}
if (!mOpened) {
return false;
return mResult;
}
// Now that we're open, we're guarnateed a Close() call. However, we are
@ -402,7 +404,7 @@ public:
// is closed, so we do that ourselves and Release() in OnClose().
aCloser->Init(this);
AddRef();
return true;
return JS::AsmJSCache_Success;
}
// This method must be called if BlockUntilOpen returns 'true'. AutoClose
@ -416,7 +418,8 @@ protected:
: mMutex("File::mMutex"),
mCondVar(mMutex, "File::mCondVar"),
mWaiting(false),
mOpened(false)
mOpened(false),
mResult(JS::AsmJSCache_InternalError)
{ }
~File()
@ -428,15 +431,16 @@ protected:
void
OnOpen()
{
Notify(true);
Notify(JS::AsmJSCache_Success);
}
void
OnFailure()
OnFailure(JS::AsmJSCacheResult aResult)
{
FileDescriptorHolder::Finish();
MOZ_ASSERT(aResult != JS::AsmJSCache_Success);
Notify(false);
FileDescriptorHolder::Finish();
Notify(aResult);
}
void
@ -455,7 +459,7 @@ protected:
private:
void
Notify(bool aSuccess)
Notify(JS::AsmJSCacheResult aResult)
{
MOZ_ASSERT(NS_IsMainThread());
@ -463,7 +467,8 @@ private:
MOZ_ASSERT(mWaiting);
mWaiting = false;
mOpened = aSuccess;
mOpened = aResult == JS::AsmJSCache_Success;
mResult = aResult;
mCondVar.Notify();
}
@ -471,6 +476,7 @@ private:
CondVar mCondVar;
bool mWaiting;
bool mOpened;
JS::AsmJSCacheResult mResult;
};
// MainProcessRunnable is a base class shared by (Single|Parent)ProcessRunnable
@ -493,6 +499,7 @@ public:
mNeedAllowNextSynchronizedOp(false),
mPersistence(quota::PERSISTENCE_TYPE_INVALID),
mState(eInitial),
mResult(JS::AsmJSCache_InternalError),
mIsApp(false),
mHasUnlimStoragePerm(false),
mEnforcingQuota(true)
@ -582,7 +589,7 @@ protected:
// This method may be overridden, but it must be called from the overrider.
// Called by MainProcessRunnable on the main thread after a call to Fail():
virtual void
OnFailure()
OnFailure(JS::AsmJSCacheResult aResult)
{
FinishOnMainThread();
}
@ -665,6 +672,7 @@ private:
eFinished, // Terminal state
};
State mState;
JS::AsmJSCacheResult mResult;
bool mIsApp;
bool mHasUnlimStoragePerm;
@ -850,6 +858,7 @@ MainProcessRunnable::OpenCacheFileForWrite()
// enough space.
EvictEntries(mDirectory, mGroup, mOrigin, mWriteParams.mSize, mMetadata);
if (!mQuotaObject->MaybeAllocateMoreSpace(0, mWriteParams.mSize)) {
mResult = JS::AsmJSCache_QuotaExceeded;
return NS_ERROR_FAILURE;
}
}
@ -1048,7 +1057,7 @@ MainProcessRunnable::Run()
MOZ_ASSERT(NS_IsMainThread());
mState = eFinished;
OnFailure();
OnFailure(mResult);
return NS_OK;
}
@ -1168,10 +1177,10 @@ private:
}
void
OnFailure() MOZ_OVERRIDE
OnFailure(JS::AsmJSCacheResult aResult) MOZ_OVERRIDE
{
MainProcessRunnable::OnFailure();
File::OnFailure();
MainProcessRunnable::OnFailure(aResult);
File::OnFailure(aResult);
}
void
@ -1225,7 +1234,7 @@ private:
}
bool
Recv__delete__() MOZ_OVERRIDE
Recv__delete__(const JS::AsmJSCacheResult& aResult) MOZ_OVERRIDE
{
MOZ_ASSERT(!mFinished);
mFinished = true;
@ -1267,7 +1276,7 @@ private:
MOZ_ASSERT(NS_IsMainThread());
if (!SendOnOpenMetadataForRead(aMetadata)) {
unused << Send__delete__(this);
unused << Send__delete__(this, JS::AsmJSCache_InternalError);
}
}
@ -1296,7 +1305,7 @@ private:
FileDescriptor::PlatformHandleType handle =
FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc));
if (!SendOnOpenCacheFile(mFileSize, FileDescriptor(handle))) {
unused << Send__delete__(this);
unused << Send__delete__(this, JS::AsmJSCache_InternalError);
}
}
@ -1316,17 +1325,17 @@ private:
}
void
OnFailure() MOZ_OVERRIDE
OnFailure(JS::AsmJSCacheResult aResult) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mOpened);
mFinished = true;
MainProcessRunnable::OnFailure();
MainProcessRunnable::OnFailure(aResult);
if (!mActorDestroyed) {
unused << Send__delete__(this);
unused << Send__delete__(this, aResult);
}
mPrincipalHolder = nullptr;
@ -1435,12 +1444,12 @@ private:
}
bool
Recv__delete__() MOZ_OVERRIDE
Recv__delete__(const JS::AsmJSCacheResult& aResult) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mState == eOpening);
Fail();
Fail(aResult);
return true;
}
@ -1462,13 +1471,13 @@ private:
private:
void
Fail()
Fail(JS::AsmJSCacheResult aResult)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mState == eInitial || mState == eOpening);
mState = eFinished;
File::OnFailure();
File::OnFailure(aResult);
}
nsIPrincipal* const mPrincipal;
@ -1507,7 +1516,7 @@ ChildProcessRunnable::Run()
// 'this' alive until returning to the event loop.
Release();
Fail();
Fail(JS::AsmJSCache_InternalError);
return NS_OK;
}
@ -1524,7 +1533,7 @@ ChildProcessRunnable::Run()
File::OnClose();
if (!mActorDestroyed) {
unused << Send__delete__(this);
unused << Send__delete__(this, JS::AsmJSCache_Success);
}
mState = eFinished;
@ -1553,7 +1562,7 @@ DeallocEntryChild(PAsmJSCacheEntryChild* aActor)
namespace {
bool
JS::AsmJSCacheResult
OpenFile(nsIPrincipal* aPrincipal,
OpenMode aOpenMode,
WriteParams aWriteParams,
@ -1576,7 +1585,7 @@ OpenFile(nsIPrincipal* aPrincipal,
// in the middle of running JS (eval()) and nested event loops can be
// semantically observable.
if (NS_IsMainThread()) {
return false;
return JS::AsmJSCache_SynchronousScript;
}
// If we are in a child process, we need to synchronously call into the
@ -1591,11 +1600,16 @@ OpenFile(nsIPrincipal* aPrincipal,
aReadParams);
}
if (!file->BlockUntilOpen(aFile)) {
return false;
JS::AsmJSCacheResult openResult = file->BlockUntilOpen(aFile);
if (openResult != JS::AsmJSCache_Success) {
return openResult;
}
return file->MapMemory(aOpenMode);
if (!file->MapMemory(aOpenMode)) {
return JS::AsmJSCache_InternalError;
}
return JS::AsmJSCache_Success;
}
} // anonymous namespace
@ -1621,7 +1635,9 @@ OpenEntryForRead(nsIPrincipal* aPrincipal,
File::AutoClose file;
WriteParams notAWrite;
if (!OpenFile(aPrincipal, eOpenForRead, notAWrite, readParams, &file)) {
JS::AsmJSCacheResult openResult =
OpenFile(aPrincipal, eOpenForRead, notAWrite, readParams, &file);
if (openResult != JS::AsmJSCache_Success) {
return false;
}
@ -1661,7 +1677,7 @@ CloseEntryForRead(size_t aSize,
MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) == file->MappedMemory());
}
bool
JS::AsmJSCacheResult
OpenEntryForWrite(nsIPrincipal* aPrincipal,
bool aInstalled,
const char16_t* aBegin,
@ -1671,7 +1687,7 @@ OpenEntryForWrite(nsIPrincipal* aPrincipal,
intptr_t* aFile)
{
if (size_t(aEnd - aBegin) < sMinCachedModuleLength) {
return false;
return JS::AsmJSCache_ModuleTooSmall;
}
// Add extra space for the AsmJSCookieType (see OpenEntryForRead).
@ -1688,8 +1704,10 @@ OpenEntryForWrite(nsIPrincipal* aPrincipal,
File::AutoClose file;
ReadParams notARead;
if (!OpenFile(aPrincipal, eOpenForWrite, writeParams, notARead, &file)) {
return false;
JS::AsmJSCacheResult openResult =
OpenFile(aPrincipal, eOpenForWrite, writeParams, notARead, &file);
if (openResult != JS::AsmJSCache_Success) {
return openResult;
}
// Strip off the AsmJSCookieType from the buffer returned to the caller,
@ -1700,7 +1718,7 @@ OpenEntryForWrite(nsIPrincipal* aPrincipal,
// The caller guarnatees a call to CloseEntryForWrite (on success or
// failure) at which point the file will be closed
file.Forget(reinterpret_cast<File**>(aFile));
return true;
return JS::AsmJSCache_Success;
}
void

View File

@ -112,7 +112,7 @@ void
CloseEntryForRead(size_t aSize,
const uint8_t* aMemory,
intptr_t aHandle);
bool
JS::AsmJSCacheResult
OpenEntryForWrite(nsIPrincipal* aPrincipal,
bool aInstalled,
const char16_t* aBegin,
@ -178,6 +178,13 @@ struct ParamTraits<mozilla::dom::asmjscache::WriteParams>
static void Log(const paramType& aParam, std::wstring* aLog);
};
template <>
struct ParamTraits<JS::AsmJSCacheResult> :
public ContiguousEnumSerializer<JS::AsmJSCacheResult,
JS::AsmJSCache_MIN,
JS::AsmJSCache_LIMIT>
{ };
} // namespace IPC
#endif // mozilla_dom_asmjscache_asmjscache_h

View File

@ -5,6 +5,7 @@
include protocol PContent;
using mozilla::dom::asmjscache::Metadata from "mozilla/dom/asmjscache/AsmJSCache.h";
using JS::AsmJSCacheResult from "mozilla/dom/asmjscache/AsmJSCache.h";
namespace mozilla {
namespace dom {
@ -29,7 +30,7 @@ child:
OnOpenCacheFile(int64_t fileSize, FileDescriptor fileDesc);
both:
__delete__();
__delete__(AsmJSCacheResult result);
};
} // namespace asmjscache

View File

@ -2703,7 +2703,7 @@ AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
aHandle);
}
static bool
static JS::AsmJSCacheResult
AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
bool aInstalled,
const char16_t* aBegin,

View File

@ -734,7 +734,7 @@ AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
aHandle);
}
static bool
static JS::AsmJSCacheResult
AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
bool aInstalled,
const char16_t* aBegin,
@ -745,7 +745,7 @@ AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
{
nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp();
if (!principal) {
return false;
return JS::AsmJSCache_InternalError;
}
return asmjscache::OpenEntryForWrite(principal, aInstalled, aBegin, aEnd,

View File

@ -2134,7 +2134,7 @@ struct ScopedCacheEntryOpenedForWrite
}
};
bool
JS::AsmJSCacheResult
js::StoreAsmJSModuleInCache(AsmJSParser &parser,
const AsmJSModule &module,
ExclusiveContext *cx)
@ -2144,15 +2144,15 @@ js::StoreAsmJSModuleInCache(AsmJSParser &parser,
// that can't be serialized. (This is separate from normal profiling and
// requires an addon to activate).
if (module.numFunctionCounts())
return false;
return JS::AsmJSCache_Disabled_JitInspector;
MachineId machineId;
if (!machineId.extractCurrentState(cx))
return false;
return JS::AsmJSCache_InternalError;
ModuleCharsForStore moduleChars;
if (!moduleChars.init(parser))
return false;
return JS::AsmJSCache_InternalError;
size_t serializedSize = machineId.serializedSize() +
moduleChars.serializedSize() +
@ -2160,17 +2160,17 @@ js::StoreAsmJSModuleInCache(AsmJSParser &parser,
JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
if (!open)
return false;
return JS::AsmJSCache_Disabled_Internal;
const char16_t *begin = parser.tokenStream.rawBase() + ModuleChars::beginOffset(parser);
const char16_t *end = parser.tokenStream.rawBase() + ModuleChars::endOffset(parser);
bool installed = parser.options().installedFile;
ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
if (!open(cx->global(), installed, begin, end, entry.serializedSize,
&entry.memory, &entry.handle)) {
return false;
}
JS::AsmJSCacheResult openResult =
open(cx->global(), installed, begin, end, serializedSize, &entry.memory, &entry.handle);
if (openResult != JS::AsmJSCache_Success)
return openResult;
uint8_t *cursor = entry.memory;
cursor = machineId.serialize(cursor);
@ -2178,7 +2178,7 @@ js::StoreAsmJSModuleInCache(AsmJSParser &parser,
cursor = module.serialize(cursor);
MOZ_ASSERT(cursor == entry.memory + serializedSize);
return true;
return JS::AsmJSCache_Success;
}
struct ScopedCacheEntryOpenedForRead

View File

@ -1538,7 +1538,7 @@ class AsmJSModule
};
// Store the just-parsed module in the cache using AsmJSCacheOps.
extern bool
extern JS::AsmJSCacheResult
StoreAsmJSModuleInCache(AsmJSParser &parser,
const AsmJSModule &module,
ExclusiveContext *cx);

View File

@ -1874,7 +1874,7 @@ class MOZ_STACK_CLASS ModuleCompiler
return module_->addBuiltinThunkCodeRange(builtin, begin->offset(), pret->offset(), end);
}
void buildCompilationTimeReport(bool storedInCache, ScopedJSFreePtr<char> *out) {
void buildCompilationTimeReport(JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char> *out) {
ScopedJSFreePtr<char> slowFuns;
#ifndef JS_MORE_DETERMINISTIC
int64_t usecAfter = PRMJ_Now();
@ -1895,10 +1895,39 @@ class MOZ_STACK_CLASS ModuleCompiler
return;
}
}
const char *cacheString = "";
switch (cacheResult) {
case JS::AsmJSCache_Success:
cacheString = "stored in cache";
break;
case JS::AsmJSCache_ModuleTooSmall:
cacheString = "not stored in cache (too small to benefit)";
break;
case JS::AsmJSCache_SynchronousScript:
cacheString = "unable to cache asm.js in synchronous scripts; try loading "
"asm.js via <script async> or createElement('script')";
break;
case JS::AsmJSCache_QuotaExceeded:
cacheString = "not enough temporary storage quota to store in cache";
break;
case JS::AsmJSCache_Disabled_Internal:
cacheString = "caching disabled by internal configuration (consider filing a bug)";
break;
case JS::AsmJSCache_Disabled_ShellFlags:
cacheString = "caching disabled by missing command-line arguments";
break;
case JS::AsmJSCache_Disabled_JitInspector:
cacheString = "caching disabled by active JIT inspector";
break;
case JS::AsmJSCache_InternalError:
cacheString = "unable to store in cache due to internal error (consider filing a bug)";
break;
case JS::AsmJSCache_LIMIT:
MOZ_CRASH("bad AsmJSCacheResult");
break;
}
out->reset(JS_smprintf("total compilation time %dms; %s%s",
msTotal,
storedInCache ? "stored in cache" : "not stored in cache",
slowFuns ? slowFuns.get() : ""));
msTotal, cacheString, slowFuns ? slowFuns.get() : ""));
#endif
}
@ -8619,10 +8648,10 @@ CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList,
if (!FinishModule(m, &module))
return false;
bool storedInCache = StoreAsmJSModuleInCache(parser, *module, cx);
JS::AsmJSCacheResult cacheResult = StoreAsmJSModuleInCache(parser, *module, cx);
module->staticallyLink(cx);
m.buildCompilationTimeReport(storedInCache, compilationTimeReport);
m.buildCompilationTimeReport(cacheResult, compilationTimeReport);
*moduleOut = module.forget();
return true;
}

View File

@ -5237,6 +5237,21 @@ typedef bool
typedef void
(* CloseAsmJSCacheEntryForReadOp)(size_t size, const uint8_t *memory, intptr_t handle);
/* The list of reasons why an asm.js module may not be stored in the cache. */
enum AsmJSCacheResult
{
AsmJSCache_MIN,
AsmJSCache_Success = AsmJSCache_MIN,
AsmJSCache_ModuleTooSmall,
AsmJSCache_SynchronousScript,
AsmJSCache_QuotaExceeded,
AsmJSCache_Disabled_Internal,
AsmJSCache_Disabled_ShellFlags,
AsmJSCache_Disabled_JitInspector,
AsmJSCache_InternalError,
AsmJSCache_LIMIT
};
/*
* This callback represents a request by the JS engine to open for writing a
* cache entry of the given size for the given global and char range containing
@ -5252,7 +5267,7 @@ typedef void
* the principal of 'global' where it will not be evicted until the associated
* installed JS file is removed.
*/
typedef bool
typedef AsmJSCacheResult
(* OpenAsmJSCacheEntryForWriteOp)(HandleObject global, bool installed,
const char16_t *begin, const char16_t *end,
size_t size, uint8_t **memory, intptr_t *handle);

View File

@ -5183,32 +5183,32 @@ ShellCloseAsmJSCacheEntryForRead(size_t serializedSize, const uint8_t *memory, i
close(handle);
}
static bool
static JS::AsmJSCacheResult
ShellOpenAsmJSCacheEntryForWrite(HandleObject global, bool installed,
const char16_t *begin, const char16_t *end,
size_t serializedSize, uint8_t **memoryOut, intptr_t *handleOut)
{
if (!jsCachingEnabled || !jsCacheAsmJSPath)
return false;
return JS::AsmJSCache_Disabled_ShellFlags;
// Create the cache directory if it doesn't already exist.
struct stat dirStat;
if (stat(jsCacheDir, &dirStat) == 0) {
if (!(dirStat.st_mode & S_IFDIR))
return false;
return JS::AsmJSCache_InternalError;
} else {
#ifdef XP_WIN
if (mkdir(jsCacheDir) != 0)
return false;
return JS::AsmJSCache_InternalError;
#else
if (mkdir(jsCacheDir, 0777) != 0)
return false;
return JS::AsmJSCache_InternalError;
#endif
}
ScopedFileDesc fd(open(jsCacheAsmJSPath, O_CREAT|O_RDWR, 0660), ScopedFileDesc::WRITE_LOCK);
if (fd == -1)
return false;
return JS::AsmJSCache_InternalError;
// Include extra space for the asmJSCacheCookie.
serializedSize += sizeof(uint32_t);
@ -5216,14 +5216,14 @@ ShellOpenAsmJSCacheEntryForWrite(HandleObject global, bool installed,
// Resize the file to the appropriate size after zeroing their contents.
#ifdef XP_WIN
if (chsize(fd, 0))
return false;
return JS::AsmJSCache_InternalError;
if (chsize(fd, serializedSize))
return false;
return JS::AsmJSCache_InternalError;
#else
if (ftruncate(fd, 0))
return false;
return JS::AsmJSCache_InternalError;
if (ftruncate(fd, serializedSize))
return false;
return JS::AsmJSCache_InternalError;
#endif
// Map the file into memory.
@ -5232,16 +5232,16 @@ ShellOpenAsmJSCacheEntryForWrite(HandleObject global, bool installed,
HANDLE fdOsHandle = (HANDLE)_get_osfhandle(fd);
HANDLE fileMapping = CreateFileMapping(fdOsHandle, nullptr, PAGE_READWRITE, 0, 0, nullptr);
if (!fileMapping)
return false;
return JS::AsmJSCache_InternalError;
memory = MapViewOfFile(fileMapping, FILE_MAP_WRITE, 0, 0, 0);
CloseHandle(fileMapping);
if (!memory)
return false;
return JS::AsmJSCache_InternalError;
#else
memory = mmap(nullptr, serializedSize, PROT_WRITE, MAP_SHARED, fd, 0);
if (memory == MAP_FAILED)
return false;
return JS::AsmJSCache_InternalError;
#endif
// The embedding added the cookie so strip it off of the buffer returned to
@ -5249,7 +5249,7 @@ ShellOpenAsmJSCacheEntryForWrite(HandleObject global, bool installed,
MOZ_ASSERT(*(uint32_t *)memory == 0);
*memoryOut = (uint8_t *)memory + sizeof(uint32_t);
*handleOut = fd.forget();
return true;
return JS::AsmJSCache_Success;
}
static void